86

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.

edwinksl
  • 23,789
Lan...
  • 911
  • 8
    The reason that you didn't find anything posted on Google was because of the Minus(-) Sign. Adding '-' before a word in a query will tell a Google to ignore pages that use that word prominently. try sh "-c" – Mohamed Mar 28 '20 at 15:43
  • 1
    I use duckduckgo.com when Google doesn't help me finding programming answers – Ricardo Nov 23 '20 at 19:22

3 Answers3

69

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
Ricardo
  • 139
Zanna
  • 70,465
  • Can you provide an example of how to use sh -c? Would be extra nice if it demonstrates the assignment of positional parameters that are mentioned in the -c part of man sh. – edwinksl Oct 01 '16 at 07:49
  • Here, single quotes are not used, but doubles: sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" – Timo Jan 24 '21 at 20:31
33

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"
L. D. James
  • 25,036
  • 4
    That answer is in the most simple manner explains the use of -c parameter - simply changing the input source for sh command from stdin to the input argument of string type. – rila Nov 29 '20 at 15:30
20

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 ...
heemayl
  • 91,753