What is the sh -c command?

I discovered the sh -c command. I found it before I posted here but I can't find any posts from Google that explain it, so I would like to know what it is and what its complete syntax is.


Solution 1:

sh calls the program sh as interpreter and the -c flag means execute the following command as interpreted by this program.

In Ubuntu, sh is usually symlinked to /bin/dash, meaning that if you execute a command with sh -c the dash shell will be used to execute the command instead of bash. The shell called with sh depends on the symlink - you can find out with readlink -e $(which sh). You should use sh -c when you want to execute a command specifically with that shell instead of bash.

You can use this syntax (the -c flag) with other interpreters too. One classic use of it (pointed out by @edwinksl is to get around the problem of redirection not working with sudo (here you could use bash -c or sh -c)

sudo sh -c 'echo "foo" > /home/bar'

will write the file bar containing the text foo to /home/, while sudo echo "foo" > /home/bar fails as explained very well here

It's important to use 'single quotes' around the command string, otherwise the current shell will try to expand it before it's passed to the interpreter you called. For example with python:

$ python3 -c print (35/7)
bash: syntax error near unexpected token `('
$ python3 -c 'print (35/7)'
5.0

Solution 2:

The -c argument is:

Read commands from the command_string operand instead of from the standard input. Special parameter 0 will be set from the command_name operand and the positional parameters ($1, $2, etc.) set from the remaining argument operands.

Other details of the sh arugments can be found by running:

$ man sh

An example of using a string as an argument is:

$ sh -c "echo This is a test string"

This is a more detailed sh -c example. It will download a document from Google Drive and open it up for editing on the desktop:

$ sh -c "wget 'https://docs.google.com/document/u/0/d/1jcBtdlMx0f4BhCmAmnIViIy4WN4oRevWFRzse-P00j0/export?format=docx' -O test.docx && xdg-open test.docx 2>/dev/null"

Solution 3:

sh -c spawns a non-login, non-interactive session of sh (dash in Ubuntu).

The command following that will be run in that shell session, it will be treated as argument (positional parameter) 0 (ARGV0), and the remaining portion as the argument to that command (ARGV0), starting from 1 (ARGV1, ARGV2, ...).

You can also use typical shell features allowed to run in this kind of session e.g. command separation using ; to use multiple commands, command grouping using {}, spawn another subshell with (), and so on. Usage of these can slightly change the argument definitions/examples mentioned earlier.


Just to note, the features that are specific to interactive shells only (by default), e.g. history expansion, source-ing of ~/.bashrc and /etc/bash.bashrc etc will not be available in this session as it is non-interactive. You can simulate an interactive sessions behavior (almost), by using the -i option:

sh -ic ...

Similarly, the features that are specific to login shells only (by default) e.g. source-ing of ~/.profile (given ~/.bash_profile and ~/.bash_login do not exist) and /etc/profile will not be done as the shell is a non-login shell. You can simulate login-shells behavior using the -l option:

sh -lc ...

To simulate both login and interactive sessions:

sh -lic ...