Bash -c with positional parameters

That gives you an opportunity to set/choose $0 when using an inline script. Otherwise, $0 would just be bash.

Then you can do for instance:

$ echo foo > foo
$ bash -c 'wc -c < "${1?}"' getlength foo
4
$ rm -f bar
$ bash -c 'wc -c < "${1?}"' getlength bar
getlength: bar: No such file or directory
$ bash -c 'wc -c < "${1?}"' getlength
getlength: 1: parameter not set

Not all shells used to do that. The Bourne shell did. The Korn (and Almquist) shell chose to have the first parameter go to $1 instead. POSIX eventually went for the Bourne way, so ksh and ash derivatives reverted to that later (more on that at http://www.in-ulm.de/~mascheck/various/find/#shell). That meant that for a long time for sh (which depending on the system was based on the Bourne, Almquist or Korn shell), you didn't know whether the first argument went into $0 or $1, so for portability, you had to do things like:

sh -c 'echo foo in "$1"' foo foo

Or:

sh -c 'shift "$2"; echo txt files are "$@"' tentative-arg0 3 2 *.txt

Thankfully, POSIX has specified the new behavior where the first argument goes in $0, so we can now portably do:

sh -c 'echo txt files are "$@"' meaningful-arg0-for-error *.txt

This behaviour is defined by POSIX:

sh -c command_name [argument...]

Read commands from the command_string operand. Set the value of special parameter 0 (see Special Parameters) from the value of the command_name operand and the positional parameters ($1, $2, and so on) in sequence from the remaining argument operands.

As for why you'd want that behaviour: this smooths out the gap between a script and a -c string. You can directly convert between the two without any change of behaviour. Other areas rely these being identical.

It's also in line with how program arguments work in general: this ultimately comes down to calling one of the exec functions, in which the first provided argument is also $0, and equally commonly that argument is the same as the executable you're running. Sometimes, though, you want a special value there, and there'd just be no other way to get it. Given that the argument exists, it has to map to something, and the user needs to be able to set what that is.

This consistency (and likely historical accident) leads to the situation you find.

Tags:

Bash