12

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?

datsb
  • 183
  • 1
  • 1
  • 10
  • 4
    Is the unmodifiable child script sourced or called? – icarus Dec 25 '19 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 Dec 25 '19 at 15:08
  • @icarus the unmodifiable child script is sourced. I can call it if if needed. Will this accomplish my objective, which is `dirname $0` returning the callee's directory instead of the caller's directory? – datsb Dec 25 '19 at 19:49
  • @terdon You are correct about "attempt to source a directory", I forgot to append yet another (unmodifiable) shell script to it. I want `$0` to have the unmodifieable script's directory rather then the modifiable script's directory. – datsb Dec 25 '19 at 19:59
  • 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 Dec 25 '19 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 Dec 25 '19 at 23:25
  • 1
    Related: https://unix.stackexchange.com/q/250681/63149 – RJHunter Dec 26 '19 at 00:28
  • 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. – Philip Couling Dec 26 '19 at 00:41
  • ah yes, execve launches the interpreter in the shebang, that makes it harder to spoof $0 – Jasen Dec 26 '19 at 00:47

3 Answers3

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
John
  • 16,759
  • 1
  • 34
  • 43
  • 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 Dec 25 '19 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 :( –  Dec 26 '19 at 01:09
18

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}
2

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.

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