What Is /Bin/Sh -C

What is /bin/sh -c?

From the man-page of bash:

-c string

If the -c option is present, then commands are read from string. If there are arguments after the string, they are assigned to the positional parameters, starting with $0.

Example:

$ bash -c ls

will launch bash and execute the command ls.

/bin/sh is usually a symlink to a shell.

Why use /bin/bash -c $command instead of calling $command directly?

Without specific examples it's hard to tell, but a common reason for doing this is that you want to make use of shell i/o redirection, pipes, etc. For example, this fragment of a Kubernetes pod manifest would fail because it involves a pipe, which requires the shell to execute the command line:

containers:
image: docker.io/alpine:latest
command:
- echo hello world | sed s/world/container/

But this would work:

containers:
image: docker.io/alpine:latest
command:
- /bin/sh
- -c
- echo hello world | sed s/world/container/

This is one relatively common situation in which you'll see things explicitly execute with a shell. If you'd like to update your question with some specific examples, we can provide a more thorough answer.


Your example is very close to what I've already included here in my answer. The command . /config/dynamicenv.sh && /app/bin/docker-entrypoint server isn't a simple command; it's a shell script that makes use of both the . and the && operators.

If they were to write:

command: [". /config/dynamicenv.sh && /app/bin/docker-entrypoint server"]

It would fail with an error along the lines of:

exec: "[\". /config/dynamicenv.sh && /app/bin/docker-entrypoint server\"]": stat [". /config/dynamicenv.sh && /app/bin/docker-entrypoint server"]: no such file or directory: unknown.

The command needs to be wrapped with sh -c in order to execute correctly.

/bin/sh 'ls -l -R' vs /bin/sh -c 'ls -l -R'

On AIX, /bin/sh defaults to the Korn Shell (ksh).

/bin/sh 'ls -l -R'

With ksh, this runs the shell, telling it to execute the script named ls -l -R (in the current directory or on the path). If no script can be found with this name, ksh treats the argument as a command and runs it.

Note that with bash, if the script cannot be found, this would result in an error.

/bin/sh -c 'ls -l -R'

This starts a new instance of the shell, telling it to run the command ls -l -R.

Difference between '-- /bin/sh -c ls' vs 'ls' when setting a command in kubectl?

At a low level, every (Unix/Linux) command is invoked as a series of "words". If you type a command into your shell, the shell does some preprocessing and then creates the "words" and runs the command. In Kubernetes command: (and args:) there isn't a shell involved, unless you explicitly supply one.

I would default to using the list form unless you specifically need shell features.

command: # overrides Docker ENTRYPOINT
- the_command
- --an-argument
- --another
- value

If you use list form, you must explicitly list out each word. You may use either YAML block list syntax as above or flow list syntax [command, arg1, arg2]. If there are embedded spaces in a single item [command, --option value] then those spaces are included in a single command-line option as if you quoted it, which frequently confuses programs.

You can explicitly invoke a shell if you need to:

command:
- sh
- -c
- the_command --an-argument --another value

This command is in exactly three words, sh, the option -c, and the shell command. The shell will process this command in the usual way and execute it.

You need the shell form only if you're doing something more complicated than running a simple command with fixed arguments. Running multiple sequential commands c1 && c2 or environment variable expansion c1 "$OPTION" are probably the most common ones, but any standard Bourne shell syntax would be acceptable here (redirects, pipelines, ...).

Kubernetes: when do i need to add /bin/sh -c to my command?

Because in first example

kubectl create job pi  --image=perl -- perl -Mbignum=bpi -wle 'print bpi(2000)'

you call a perl interpreter, perl -Mbignum=bpi -wle 'print bpi(2000)'

and in second example you call a bash shell command, echo hello;sleep 30;echo world therefore /bin/sh -c

kubectl create job busybox --image=busybox -- /bin/sh -c 'echo hello;sleep 30;echo world'

The -- is saying after this it is a command is executed inside the container.

/bin/sh: -c: line 0: syntax error near unexpected token `('

Python's call() function invokes the command with sh by default. The process substitution syntax is supported by bash, but not by sh.

$ sh -c "cat <(date)"  
sh: -c: line 0: syntax error near unexpected token `('
sh: -c: line 0: `cat <(date)'

$ bash -c "cat <(date)"
Mon Mar 14 11:12:48 PDT 2022

If you really need to use the bash-specific syntax, you should be able to specify the shell executable (but I have not tried this):

subprocess.call(arg2, shell=True, executable='/bin/bash')

Executing `sh -c` in a bash script


TL;DR:

This is a typical use case (perform some business logic in a Docker entrypoint script before running a compound command, given at command line) and the recommended last line of the script is:

exec "$@"

Details

To further explain this line, some remarks and hyperlinks:

  1. As per the Bash user manual, exec is a POSIX shell builtin that replaces the shell [with the command supplied] without creating a new process.

  2. As a result, using exec like this in a Docker entrypoint context is important because it ensures that the CMD program that is executed will still have PID 1 and can directly handle signals, including that of docker stop (see also that other SO answer: Speed up docker-compose shutdown).

  3. The double quotes ("$@") are also important to avoid word splitting (namely, ensure that each positional argument is passed as is, even if it contains spaces). See e.g.:

     #!/usr/bin/env bash

    printargs () { for arg; do echo "$arg"; done; }

    test0 () {
    echo "test0:"
    printargs $@
    }

    test1 () {
    echo "test1:"
    printargs "$@"
    }

    test0 /bin/sh -c 'echo "ok"'
    echo
    test1 /bin/sh -c 'echo "ok"'
    test0:
    /bin/sh
    -c
    echo
    "ok"

    test1:
    /bin/sh
    -c
    echo "ok"
  4. Finally eval is a powerful bash builtin that is (1) unneeded for your use case, (2) and actually not advised to use in general, in particular for security reasons. E.g., if the string argument of eval relies on some user-provided input… For details on this issue, see e.g. https://mywiki.wooledge.org/BashFAQ/048 (which recaps the few situations where one would like to use this builtin, typically, the command eval "$(ssh-agent -s)").



Related Topics



Leave a reply



Submit