How to change argv[0] value using bash/shell?

2019-08-02 11:35发布

问题:

The script should be able to change argv[0] value in shell / bash script. I found on an older post but couldn't really understand what it is doing. Could someone please explain how the line works:

sh -c ". '$0'" argv0new "$@"

Also is test ".$INNERCALL" meant to be a variable ?

Original question: How to change argv[0] value in shell / bash script?

#! /bin/bash    # try executing this script with several arguments to see the effect

test ".$INNERCALL" = .YES || {
    export INNERCALL=YES
    # this method works both for shell and bash interpreters
    sh -c ". '$0'" argv0new "$@"
    # bash -c ". '$0'" argv0new "$@"
    exit $?
}

printf "argv[0]=$0\n"
i=1 ; for arg in "$@" ; do printf "argv[$i]=$arg\n" ; i=`expr $i + 1` ;done

回答1:

Let's go through this line by line

test ".$INNERCALL" = .YES

basically this sees if $INNERCALL already has the value YES in it. Shell's "" function does variable expansion in a safe way, and marshalls it all into a value, e.g.

foo="hello"
bar=", world!"
echo "foobar is: $foo$bar"

prints foobar is: hello, world!


|| {
...
}

This uses the || operator to say, if the previous program returned with a non-zero value (e.g. test is a program that returns 1 if the associated conditional is false), then execute this code block, otherwise skip it (see this link for more on ||)


export INNERCALL=YES

This sets INNERCALL to YES, which means that this only executes for the first level


sh -c ". '$0'" argv0new "$@"
exit $?

This is where the magic happens. sh -c opens a new shell, which then reads its arguments from the string that follows. ". '$0'" uses sh's source function on the value currently at $0, which is supposed to be this current file.

Basically sh -c ". '$0'" just opens the current file again in a child sh process, then the rest of the line replaces the arguments: argv0new becomes the new $0, and you keep the original arguments to the file by also including "$@"

then exit $? returns whatever return value the child process runs.

the rest of the code is just to prove that all of the arguments except for $0 is the same, and $0 has been replaced.


tl;dr it opens up a child shell process, tells it to read the current file, replaces the arguments, and then exports a test value so that it doesn't infinite loop.



回答2:

The script calls itself with a particular set of parameters when the variable INNERCALL is unset. It sets the variable to avoid an infinite loop, then calls itself in a way which allows the script to set its own $0. The inner instance then executes the code outside of the test, which demonstrates that $0 is now indeed set to a particular value which the script's author chose. When this finishes, we return to the outer instance of the script, which then simply exits.

The real beef is that sh -c 'script...' arg0 arg1 arg2 sets $0 to the first argument after the script itself (arg0 in this example).



回答3:

note it doesn't change the value of argv0 but create a new process with argv0new

changing

sh -c ". '$0'" argv0new "$@"

by

exec sh -c ". '$0'" argv0new "$@"

would also work changing bash by sh.

For bash From bash Invoking-Bash

-c

Read and execute commands from the first non-option argument command_string, then exit. If there are arguments after the command_string, the first argument is assigned to $0 and any remaining arguments are assigned to the positional parameters. The assignment to $0 sets the name of the shell, which is used in warning and error messages.



标签: bash shell sh