user3655264
user3655264

Reputation: 72

Empty Positional Argument when using /bin/bash -c <script>

I'm trying to launch a script with /bin/bash -c with positional arguments but can't figure out the following issue:

Suppose I have test.sh as follows:

#!/bin/bash
echo $0
echo $1
> ./test.sh a
./test.sh
a

> /bin/bash -c ./test.sh a
./test.sh

Why does the second one return an empty position argument for $1? Based on the man page:

   -c        If the -c option is present, then commands are read from the first non-option argument command_string.  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.

It seems like "a" should be assigned to $0 at least, which is not what I saw. /bin/bash -c 'echo $0' a works as expected. Thanks!

Upvotes: 2

Views: 605

Answers (3)

Demarco Glover
Demarco Glover

Reputation: 27

bash [long-opt] [-abefhkmnptuvxdBCDHP] [-o option] [-O shopt_option] -c string [argument ...]

-c supposed to be followed by a string, so you may quote ./test.sh a like:

$ /bin/bash -c "./test.sh a"
./test.sh
a

Upvotes: 1

user1934428
user1934428

Reputation: 22311

The -c option does not collect all following arguments of the bash command, but just uses the first non-option argument, which in your case is the one immediately following it. I don't see why you want to use -c here. I would write your command as

/bin/bash test.sh a

Since in this case, no PATH search is involved, you can also omit the ./ part. In fact, test.sh doesn't even need to be executable here.

Upvotes: 0

Gordon Davisson
Gordon Davisson

Reputation: 126038

The string after -c acts like a miniature script, and the arguments after that are passed to it as $0, $1, $2, etc. For example:

$ bash -c 'echo "\$0=$0, \$1=$1, \$2=$2"' zero one two
$0=zero, $1=one, $2=two

(Note: it's important that the mini-script is in single-quotes; without them the references to $0 would be expanded by your interactive shell before they even get passed to the bash -c command.)

In your case, the mini-script runs another script (./test.sh), but doesn't pass on the arguments. If you wanted to pass them on, you'd do something like this:

$ bash -c './test.sh "$1" "$2"' zero one two
./test.sh
one

If the script had bothered to print its $2 here, it would've gotten "two". It doesn't help to pass on $0, because for a real script that's automatically set to the actual command used to run the script.

Upvotes: 2

Related Questions