1

How can I repeat a shell command using brace expansion (or another simple method), adjusting certain parameters for each iteration of the command? Here's a simple example of what I'd like to do (but with more lengthy commands): cp a b; cp a c;

Obviously, that works if I type that into a terminal. But I'd like to be able to generate that in a more compact way. I tried to use bash brace expansion but it gives me a command not found error:

~/test $ ls
a
~/test $ cp\ a\ {b,c}\;
cp a b;: command not found
~/test $ 

Using backticks also doesn't work.

~/test $ `cp\ a\ {b,c}\;`
cp a b;: command not found
mike
  • 219
  • 2
  • 9
  • 1
    You are constructing a single string of `cp a b;`. That's not a command. That's a string. Try typing "cp a b;" at your shell and seeing what happens. You can't do what you are trying to do here without `eval`. `echo b c | xargs cp a` should do what you are trying to do here but without knowing what the *real* situation is I can't offer a better solution. – Etan Reisner Sep 23 '14 at 15:48
  • 1
    @etan good advice. A more explicit way to write that is `for f in b c; do cp a $f; done`, but that's just a matter of style really. – glenn jackman Sep 23 '14 at 16:59
  • `eval cp\ a\ {b,c}\;` seems to work. What exactly is the difference between backticks and $() on the one hand and eval on the other? – mike Sep 23 '14 at 19:42
  • 1
    The shell applies various types of expansions to each word of an input line in order to evaluate the eventual arguments of a command. When that command is `eval`, the resulting arguments undergo that entire process *again*, this time with the first argument as the command that is finally executed. – chepner Sep 23 '14 at 22:56
  • @mike, using backticks as given here reads the stdout of the cp command and tries to run that stdout as another command itself, thus having no effect at all in the common case where cp emits content to stderr only and nothing to stdout. – Charles Duffy Sep 23 '14 at 23:45
  • ...what backticks and `$()` do is called _comand substitution_; it substitutes the output of the subcommand given inside of it into the larger command being run by the line. – Charles Duffy Sep 23 '14 at 23:50

1 Answers1

1

Doing this in a correct and safe manner is not as terse as you'd like:

for arg in b c; do cp a "$arg"; done

The quotes are important -- otherwise, a file named filename with a space will be converted into four separate arguments, filename, with, a and space. (If you're literally only processing b and c, that's not an issue, but establishing habits that would get you in hot water later isn't good practice).


Your question and comments posit use of eval to process the result of a brace expansion, but that's somewhat evil:

eval "cp a "{b,c}";"

The problem with this is that while it works well with very simple parameters, it will behave in surprising and unexpected ways with arguments that aren't so simple (filenames that contain shell syntax, in particular). As such, it's extremely poor practice.


With respect to backticks --

`cp a b c`

is exactly the same as

cp a b c

...except that it also reads from cp's stdout, substitutes the output of cp into the resulting command, and then runs that command as well.

So,

echo `cp a b c`

first runs cp a b c, reads the output -- which is empty, since even if there's an error, it's written to stderr, not stdout, and then runs

echo

...with no arguments. If cp did emit something to its stdout, then that string would be parsed into arguments and those arguments would be passed to echo.

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • Thanks for your extensive explanation. Very helpful. Not as compact as I was hoping but it does work. – mike Sep 24 '14 at 10:35
  • However, I still don't understand why the backticks don't work. From your explanation (and assuming it does the brace expansion first), `\`cp\ a\ {b,c}\;\`` (with backticks) should `run cp a b; a c;` (expanded and without backticks) and then, since there's nowhere to substitute it, do nothing? But instead, it gives me an error. Perhaps bash doesn't do brace expansion within backticks? – mike Sep 24 '14 at 10:36
  • Also, by the way, I was trying to run `cp a b; cp a c;` not `cp a b c` as you showed in your backticks explanation. Not sure if you changed that on purpose. – mike Sep 24 '14 at 10:39
  • @mike, again, `\`cp\ a\ {b,c}\;\`` is exactly the same as `cp\ a\ {b,c}\;` -- the backticks don't make any difference except adding an extra step where the command's output is again run as another command. – Charles Duffy Sep 24 '14 at 14:10
  • @mike, ...so, for that reason, the whitespace within the command isn't broken into separate arguments (which is a syntactical parsing step, which happens *before* brace expansion) unless you use `eval`, which goes back to the very beginning of the parsing process. – Charles Duffy Sep 24 '14 at 14:11
  • Thanks, I think I understand better now. I misinterpreted what you meant by "`X` is exactly the same as X". I thought you meant it would run the command X, doing the brace expansion. It seems like you really meant it would run the string X as a command without expansion, which gave me the error. – mike Sep 25 '14 at 13:11
  • 1
    well -- it does the brace expansion, but it expands to a string that has spaces as data, not syntax (because when you write `\ `, that's exactly what you're telling the shell to do, and only `eval` or an equivalent can transform data back to syntax). That means it's trying to find and run a file called something like `/bin/cp a b c;`, with the spaces and semicolons part of its name, not `/bin/cp`. – Charles Duffy Sep 25 '14 at 13:22