Goal and context
I am writing a bash script (called foreach_repo
) that should execute the passed parameters as a shell command, e.g.:
foreach_repo hg status
should execute the command hg status
. (It does this on a complicated nested structure of repositories, I have no control over this structure, but I need to batch operate on them quite often).
This type of command is similar to sudo
and other 'higher-order' commands; for example sudo hg status
would in turn execute hg status
with superuser rights.
Internally, my script does the following (given a feed of repository paths on stdin, created by another part of the script - irrelevant to this question):
while read repo do
container="$repo/.."
cd $base/$container
$@
done
Where $@
is meant to interpret the passed arguments as the command to be executed in the repository.
Execution
This approach works fine for simple commands, for example
foreach_repo hg status
will correctly return the status list of every repository in the structure. However, more complicated commands (with escapes, quotes ...) are messed up. For example, when I try
foreach_repo hg commit -m "some message"
I get
abort: message: no such file or directory
Because the quotes were stripped off, the actual command executed was:
hg commit -m some message
Attempted solutions
Manually escaping the quotes, or the entire command to be passed, has no effect, neither has using $*
instead of $@
. However, commands like sudo
can handle this type of situation, that is:
sudo hg commit -m "some message"
would actually (and correctly) execute
hg commit -m "some message"
How can I accomplish the same behavior?
You are on the right track, and almost got it. You just need to use
"$@"
instead of$@
.Here's a summary of what
$*
and$@
do, with and without quotes:$*
and$@
paste in the positional arguments, then tokenise them (using$IFS
) into separate strings."$*"
pastes in the positional arguments as one string, with the first character of$IFS
(usually a space) inserted between each."$@"
pastes in the positional arguments, as a string for each argument.Examples:
Here's what changes when you set
$IFS
: