问题
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?
回答1:
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:
$ set foo bar "foo bar:baz"
$ printf "%s\n" $*
foo
bar
foo
bar:baz
$ printf "%s\n" $@
foo
bar
foo
bar:baz
$ printf "%s\n" "$*"
foo bar foo bar:baz
$ printf "%s\n" "$@"
foo
bar
foo bar:baz
Here's what changes when you set $IFS:
$ IFS=:
$ printf "%s\n" $*
foo
bar
foo bar
baz
$ printf "%s\n" $@
foo
bar
foo bar
baz
$ printf "%s\n" "$*"
foo:bar:foo bar:baz
$ printf "%s\n" "$@"
foo
bar
foo bar:baz
来源:https://stackoverflow.com/questions/18333070/bash-get-literal-parameters-passed-to-a-script-and-execute-them-as-a-command