14

I know that this may sound as "not as intended by designer" but I have real life situation where a bash script I am modifying needs to call another bash script that I am not allowed to modify.

That "unmodifiable bash script" starts with:

source `dirname $0`/setenv.sh

(that setenv.sh also starts with SCR2PATH=source "$( cd "$(dirname "$0")" ; pwd -P )" and is also unmodifiable)

Is there a trick in which I can fool the child script with a different $0 than that of the calling script?

9
  • 4
    Is the unmodifiable child script sourced or called?
    – icarus
    Commented Dec 25, 2019 at 15:05
  • 2
    What are you trying to achieve? What value do you want $0 to have? Can you show us the script you can actually modify? What is the source "$( cd "$(dirname "$0")" ; pwd -P )" supposed to do? That will attempt to source a directory, you should be getting an error.
    – terdon
    Commented Dec 25, 2019 at 15:08
  • 8
    @datsb so, you want one script you don't show us to source another script you don't show us which sources a third one which you also don't show us? I hope you can see how this is a little difficult to understand ;) Please edit your question and add the scripts. The best thing would be to make tiny, simple scripts that recreate the problem and show us those.
    – terdon
    Commented Dec 25, 2019 at 20:07
  • 3
    the execve system call allows setting $0 to any arbitrary value you just need a way to exploit fhat from inside your script.
    – Jasen
    Commented Dec 25, 2019 at 23:25
  • 4
    @jasen that would not work for a bash script. Bash needs to be invoked with a correct path for the script so it can load and interpret it. It is bash, not the operating system that sets $0 for the script. Commented Dec 26, 2019 at 0:41

3 Answers 3

19

Maybe this will do:

bash -c '. /path/to/unmodifiable' /fake/path args ...

Obviously, /path/to/unmodifiable should be a shell script; this silly trick won't work with other kind of executables. This also won't work with zsh (but will work with other shells like dash, ksh, etc).

If you have to source instead of call the unmodifiable script, you can use the same trick from a wrapper calling your outer script, or have it re-call itself:

$ cat unmodifiable
#! /bin/sh
printf '{%s} ' "$0" "$@"
echo

$ cat becoming
#! /bin/sh
[ "$MYSELF" ] || MYSELF=$0 exec sh -c '. "$MYSELF"' "I'm the mountain!" "$@"
. ./unmodifiable

$ chmod 755 becoming
$ ./becoming 1 2 3
{I'm the mountain!} {1} {2} {3}
1
  • Your bash -c '. /path/to/unmodifiable' /fake/path args ... trick works like a charm.
    – datsb
    Commented Dec 26, 2019 at 8:27
19

You cannot "fool" the child script, but you can call it via a different name by creating a symlink to the called script and calling the symlink. This will, I believe achieve the objective you are looking for.

Example:

$ cat ./bin/script.sh
echo $0

$ ./bin/script.sh
./bin/script.sh
$ ln -s bin/script.sh foo.sh
$ ./foo.sh
./foo.sh
2
  • I may have described my objective unclearly: foo.sh is in the directory that I need to be returned by dirname. script.sh (which calls foo.sh) is in a different directory. When script.sh calls foo.sh (using source), the dirname $0 in foo.sh reports script.sh's directory, not foo.sh's -- which is exactly the opposite of what's needed.
    – datsb
    Commented Dec 25, 2019 at 19:46
  • 1
    so symlink it into the directory you want... assuming that directory is writable by you. If it is not, then I think you're out of luck :(
    – user339730
    Commented Dec 26, 2019 at 1:09
4

You can use exec -a in a subshell: (exec -a fakeDollarZero /unmodifiable/bash/script the rest of the args)

Unlike mosvy's answer, this would work even if it were something other than a bash script.

1
  • this does not work with executable scripts (no matter the language they're written in), but only with binaries.
    – user313992
    Commented Jan 16, 2020 at 20:07

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .