3

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 Answer 1

7

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 
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.