The -c
option allows programs to run commands.
It’s a lot easier to fork and do
execl("/bin/sh", "sh", "-c", "date | od -cb && ps > ps.out", NULL);
than it is to fork, create a pipe, fork again,
call execl
in each child, call wait
, check the exit status,
fork again, call close(1)
, open the file,
ensure that it is open on file descriptor 1, and do another execl
.
I believe that this was the reason why the option was created
in the first place.
The system()
library function runs a command by the above method.
- It provides a way to take an arbitrarily complex command
and make it look like a simple command.
This is useful with programs that run a user-specified command,
such as
find … -exec
or xargs
.
But you already knew that; it was part of the answer to your question,
How to specify a compound command as an argument to another command?
It can come in handy
if you’re running an interactive shell other than bash.
Conversely, if you are running bash, you can use this syntax
$ ash -c "command"
︙
$ csh -c "command"
︙
$ dash -c "command"
︙
$ zsh -c "command"
︙
to run one command in another shell,
as all of those shells also recognize the -c
option.
Of course you could achieve the same result with
$ ash
ash$ command
︙
ash$ exit
$ csh
csh$ command
︙
csh$ exit
$ dash
dash$ command
︙
dash$ exit
$ zsh
zsh$ command
︙
zsh$ exit
I used ash$
, etc., to illustrate the prompts from the different shells;
you probably wouldn’t actually get those.
It can come in handy
if you want to run one command in a “fresh” bash shell; for example,
$ ls -lA
total 0
-rw-r--r-- 1 gman gman 0 Apr 14 20:16 .file1
-rw-r--r-- 1 gman gman 0 Apr 14 20:16 file2
$ echo *
file2
$ shopt -s dotglob
$ echo *
.file1 file2
$ bash -c "echo *"
file2
or
$ type shift
shift is a shell builtin
$ alias shift=date
$ type shift
shift is aliased to ‘date’
$ bash -c "type shift"
shift is a shell builtin
The above is a misleading over-simplification.
When bash is run with -c
, it is considered a non-interactive shell,
and it does not read ~/.bashrc
, unless is -i
specified. So,
$ type cp
cp is aliased to ‘cp -i’ # Defined in ~/.bashrc
$ cp .file1 file2
cp: overwrite ‘file2’? n
$ bash -c "cp .file1 file2"
# Existing file is overwritten without confirmation!
$ bash -c -i "cp .file1 file2"
cp: overwrite ‘file2’? n
You could use -ci
, -i -c
or -ic
instead of -c -i
.
This probably applies to some extent to the other shells
mentioned in paragraph 3, so the long form (i.e., the second form,
which is actually exactly the same amount of typing) might be safer, especially if you have initialization/configuration files
set up for those shells.
As Wildcard explained, since you’re running a new process tree
(a new shell process and, potentially, its child process(es)),
changes to the environment made in the subshell
cannot affect the parent shell (current directory,
values of environment variables, function definitions, etc.)
Therefore, it’s hard to imagine a shell builtin command
that would be useful when run by sh -c
.
fg
, bg
, and jobs
cannot affect or access background jobs
started by the parent shell, nor can wait
wait for them.
sh -c "exec some_program"
is essentially equivalent
to just running some_program
the normal way,
directly from the interactive shell.
sh -c exit
is a big waste of time.
ulimit
and umask
could change the system settings
for the child process, and then exit without exercising them.
Just about the only builtin command that would be functional
in a sh -c
context is kill
.
Of course, the commands that only produce output
(echo
, printf
, pwd
and type
) are unaffected,
and, if you write a file, that will persist.
- Of course you can use a builtin in conjunction with
an external command; e.g.,
sh -c "cd some_directory; some_program"
but you can achieve essentially the same effect with a normal subshell:
(cd some_directory; some_program)
which is more efficient.
The same (both parts) can be said for something like
sh -c "umask 77; some_program"
or ulimit
(or shopt
).
And since you can put an arbitrarily complex command after -c
—
up to the complexity of a full-blown shell script —
you might have occasion to use any of the repertoire of builtins;
e.g., source
, read
, export
, times
, set
and unset
, etc.
echo printf type pwd
are builtins; so areset shopt shift read readarray mapfile trap declare/typeset export local let readonly unset
which don't affect the parent but may still be useful inside a moderately complicated-c
. – dave_thompson_085 Apr 15 '16 at 08:06sh -c
being used with a complex command line including three programs, a pipe, a&&
, and a redirection; I showed an example of it being used with a compound command line including acd
, a;
, and a program; I showed an example of it being used withexec
; and I said it could be used with builtins likeecho
,kill
,printf
,pwd
andtype
(and alsoexit
,ulimit
, andumask
). What more do you want to know? … (Cont’d) – G-Man Says 'Reinstate Monica' Apr 15 '16 at 22:45execve()
can operate on a script?”, (1) that has nothing to do withbash -c
, and (2) what research have you done? Theexecve
man page answers this, and a search of this site yields this, this, this, this, this, and this. – G-Man Says 'Reinstate Monica' Apr 15 '16 at 22:45