Running a command with bash -c vs without
What is the difference between these two commands?
cat test.txt
bash -c "cat test.txt"
I already read the documentation but there was not any example to understand it.
Solution 1:
cat test.txt
in the shell tells it to execute the command cat
with the argument test.txt
bash -c "cat text.txt"
tells it to execute the command bash
with the arguments -c
and cat test.txt
. The latter obviously ends up repeating what was stated above once the new shell instance has been loaded.
(I believe Bash has some internal optimizations to somewhat simplify what actually happens when you ask it to run a second copy of itself, but I guess that's beside the point here really.)
There are situations where you need to specify a single command for some reason (maybe a hook in another program lets you run a command, but only one) and you need to run more than one command; or when you want to use shell features like redirection or wildcard expansion in a separate shell instance.
sudo bash -c "whoami; test [ -w / ] && echo 'Fun to be privileged!'"
sudo bash -c 'echo 1 >/proc/privileged/something' # though see also "sudo tee"
find . -name '*.txt' -exec bash -c 'mv "$1" "${1/.txt/.csv}"' _ {} \;
The final example uses the Bash-only parameter expansion ${variable/pattern/replacement}
to change .txt
to .csv
; neither find
nor mv
has any facility for doing this sort of replacement, so calling a shell to do it is easy and convenient (though maybe first check if you have a rename
command which would be able to do it in a single simple command like rename '/\.txt$/.csv/' **/*.txt
)
Also, if you are running another shell, but want to use Bash for something (there are many Bash-only features which are handy; see also Difference between sh
and bash
), this is a way to do that; though if you are doing this in a script, obviously then it would usually make more sense to write the entire script in Bash.
In isolation, just running cat test.txt
in a subshell is merely wasteful, and costs you an extra process for no actual benefit.
Perhaps tangentially note also that the double quotes cause the calling shell to perform variable substitutions in the quoted string before passing it as an argument to the executable. Single quotes are probably better if you don't specifically require this behavior.
Notice also that
bash -c ./script foo bar baz
is not equivalent to
./script foo bar baz
In fact, the former puts foo
in "$0"
! The general convention if you want to pass in arguments with bash -c
is to put _
or bash
in the first argument.
bash -c ./script bash foo bar baz
Another convention to be aware of is that login
puts a dash in front when the shell is interactive, so that you can check whether you are running an interactive shell simply by checking whether $0
starts with -
.