What is /bin/sh -c?
From the man-page of bash:
-c string
If the
-c
option is present, then commands are read fromstring
. 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:
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.As a result, using
exec
like this in a Docker entrypoint context is important because it ensures that theCMD
program that is executed will still have PID 1 and can directly handle signals, including that ofdocker stop
(see also that other SO answer: Speed up docker-compose shutdown).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"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 ofeval
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 commandeval "$(ssh-agent -s)"
).
Related Topics
Set Filetype and Comment Key Map with .S File
Printing Variable to Command Line Using Assembly in Linux
How to Wrap Lines Within Columns in Linux
Is Kernel Space Mapped into User Space on Linux X86
Why Didn't I Get Segmentation Fault When Storing Past the End of the Bss
Trying to Ping Linux Vm Hosted on Azure Does Not Work
How to Force Cp to Overwrite Without Confirmation
How to Get Sudo Access for a File Inside the Vi Text Editor
How to Make Grep Print the Lines Below and Above Each Matching Line
How to Grep a String in a Directory and All Its Subdirectories
How to Parse Command Line Options in Bash Shell
Why Do My Results Different Following Along the Tiny Asm Example
Shell Script for Multithreading a Process
Emulating Slurm on Ubuntu 16.04
How to Not Transfer Changes Done to Files from a Branch to a Another in Git
What Is the Pid in the Host, of a Process Running Inside a Docker Container