1

The bash manpage states the following for the -c option:

-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.

From the above I would expect the code below to be equivalent in its output:

# or possibly (`bash -c date date +%z`)
$ bash -c date +%z
Wed Oct 18 10:02:47 PM UTC 2023
$ date +%z
+0000
# The first command gives the output of `date` without the format specifier.

However, my bash (GNU bash, version 5.2.15) never seems to evaluate additional command line positional parameters. Everything after the first argument after the -c option is silently discarded. Perhaps I am misunderstanding the man page.

How would I pass multiple arguments to a bash -c command as words (arg vector under the hood) rather than one string (bash -c "date +%z")? Is this possible?

Context

My goal in asking this question is to understand how bash expects command parameters to be passed in order to correctly feed them into bash via one of the execve() family of functions from another program (a wrapper shell).

3 Answers 3

3

the first argument is assigned to $0

$0 is not the first argument, $1 is. Also, you need to use the arguments in the command string.

bash -c 'date "$1"' zero +%z
2
  • Well that is not what I expected from the manpage description. Thanks.
    – 111---
    Commented Oct 18, 2023 at 22:19
  • @111--- read it as "the first argument [out of 'arguments after the command_string'] is assigned to $0". It's not the first argument to the executed script.
    – muru
    Commented Oct 19, 2023 at 2:35
2

Here,

bash -c date +%z

The first non-option argument after the -c is date, that's the shell code that Bash will parse and run. The arguments after that are assigned to the shell or script name $0 (here, +%z), then the positional parameters $1, $2 ... (all unset here).

The string +%z is not discarded, but since the command date does not make use of $0 (or $1 etc.), it does not affect what the shell does. It would get used if the shell had to print an error message, however. E.g.:

$ /bin/bash -c datexxx +%z
+%z: datexxx: command not found

The script couldn't find the command datexxx, so it did output an error message, prefixing it with the name that we gave to the script (+%z).


To use the arguments that come after -c and the shell code, you'll need to pass a command that uses the script name in $0 or the positional parameters $1..., e.g.:

% bash -c 'echo "script name: $0", first arg: "$1"' foo bar doo
script name: foo, first arg: bar

Though if you want to pass the argument list to another command as-is, you'd rather use "$@" (with the quotes) which expands to all positional parameters as distinct fields (as if you'd used "$1" "$2" ... for all the set positional parameters).

So (assuming GNU date for -d):

% bash -c 'date "$@"' my-inline-script +"%F %T" -d '1 Jan 2001'
2001-01-01 00:00:00

(Again, that my-inline-script goes to $0 and gets only used if the shell needs to print an error message. The example above should show why it makes sense to put someething descriptive there. In any case, we need to pass some value there as $0 is filled before the positional parameters.)

1

The argument to -c needs to be one string. The rest will be parameters $0, $1, ...

Example:

bash -c 'echo name=$0 params=$*' a b c

Output: name=a params=b c

The use of single quotes is important here. As it does not let the current shell fill in values for $0, $* etc.

If you wanted to pick up values from the current shell use double quotes. E.g.:

X=42
bash -c "echo $X"

Output: 42

Mixing single and double quotes is also possible. As long as the argument following -c remains one string.

X=42
bash -c "echo $X"' 0=$0 $*' 

Output: 42 0=bash (here $0 defaults to bash)

3
  • Why this doesn't work here /bin/bash -c "pp=(1 2 3) && echo ${pp[*]}" ? Any idea ? Commented Nov 15, 2023 at 22:06
  • That doesn't work because the ${pp[]} is evaluated by the outer shell. Use single quotes to prevent that: /bin/bash -c 'pp=(1 2 3) && echo ${pp[]}' Commented Nov 16, 2023 at 23:17
  • Thanks. That worked. Commented Nov 18, 2023 at 19:04

You must log in to answer this question.

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