20

I'd like to know the best way of using positional parameters when using the command bash -c.

The man pages indicates for the -c option that:

If there are arguments after the command_string, they are assigned to the positional parameters, starting with $0.

So I guess that the following form is the right one:

$ bash -c 'printf "%s %s %s\n" $0 $1 $2' param1 param2 param3
param1 param2 param3

However I saw the following form, and I wonder why it would be better

$ bash -c 'printf "%s %s %s\n" $1 $2' _ param1 param2
param1 param2 

In this case, the character _ replaces $0.

I know that for many interpreters $0 has a special meaning (command name, all positional parameters...), but in this case the man pages are quite clear.

So, is there something wrong with the first form and is there any drawback of using $0 as the first positional parameter for the bash -c command?

oliv
  • 12,690
  • 25
  • 45

2 Answers2

22
bash -c 'printf "%s %s %s\n" $0 $1 $2' param1 param2 param3

The above works but many would consider it a bad habit to get into. If you were to copy code from the -c string to a script, it would fail. Similarly, if you were to copy code from a script to a -c string, it would fail.

By contrast, with the following form, $1 means the same thing in the -c string that it would mean in a script or shell function:

bash -c 'printf "%s %s %s\n" $1 $2 $3' _ param1 param2 param3

Consistency of programming style reduces bugs.

The shell treats $0 differently

One customarily refers to all of a script's arguments with $@ or $*. Note that these variables do not include $0:

$ bash -c 'echo "$*"' param1 param2 param3
param2 param3
$ bash -c 'echo "$@"' param1 param2 param3
param2 param3

$0 is the program name

In regular scripts, $0 is the name of the script. Consequently, when using bash -c, some people prefer to use some meaningful name for the $0 parameter, such as:

bash -c 'printf "%s %s %s\n" $1 $2 $3' bash param1 param2 param3

Or:

bash -c 'printf "%s %s %s\n" $1 $2 $3' printer param1 param2 param3

This approach has a clear advantage if the -c string generates an error. For example, consider this script:

$ cat script.sh
#!/bin/bash
bash -c 'grepp misspelling "$1"' BadPgm file.txt

If we run the script, the following output is produced:

$ ./script.sh 
BadPgm: grepp: command not found

This identifies the source of the error as the command in the bash -c string.

John1024
  • 109,961
  • 14
  • 137
  • 171
  • I get your point. Looks to me that the `-c` option has a bug and should have used positional parameter starting from `$1`, but I noticed that `zsh` behaves the same as `bash`. There might be some reason... – oliv Aug 10 '16 at 07:56
  • @oliv I just updated the answer with a section to discuss why many people like `$0` to behave just the way it does. – John1024 Aug 10 '16 at 08:06
8

The parameter $0 is special in that it does not participate in shift (for example).

This works as expected:

$ bash -c 'shift; printf "%s %s %s\n" $1 $2 $3' _ par1 par2 par3 par4
par2 par3 par4

This does not:

$ bash -c 'shift; printf "%s %s %s\n" $0 $1 $2' par1 par2 par3 par4
par1 par3 par4

This has been defined in POSIX (simplified):

sh -c command_string [command_name [argument...]]

The following additional options shall be supported:

-c

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.

So, use this:

sh -c 'commands' command_name arg1 arg2 arg3 ....
Community
  • 1
  • 1