bash: get literal parameters passed to a script and execute them as a command

故事扮演 提交于 2019-12-10 18:56:38

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!