4

My question is very similar to How to copy multiple files from a different directory using cp?

I don't want to use an explicit loop. Here is what I do:

$ FILES_TOOLS="fastboot,fastboot-HW.sh"
$ cp $HOME/tools/{$FILES_TOOLS} $TOP_DIR/removeme
cp: cannot stat `/home/johndoe/tools/{fastboot,fastboot-HW.sh}': No such file or directory

The files are present and destination is valid, because:

$ cp $HOME/tools/{fastboot,fastboot-HW.sh} $TOP_DIR/removeme
$ echo $?
0
  • I tried to remove the double quote from FILES_TOOLS, no luck.
  • I tried to quote and double quote {...}, no luck
  • I tried to backslash the brackets, no luck
  • I guess this is a problem of when the shell expansion actually occurs.
Community
  • 1
  • 1
m-ric
  • 5,621
  • 7
  • 38
  • 51

3 Answers3

2

This answer is limited to the bash.

Prepend an echo to see what your cp command turns into:

echo cp $HOME/tools/{$FILES_TOOLS} $TOP_DIR/removeme

You have to insert an eval inside a sub-shell to make it work:

cp $( eval echo $HOME/tools/{$FILES_TOOLS} ) $TOP_DIR/removeme
Perleone
  • 3,958
  • 1
  • 26
  • 26
1

I guess this is a problem of when the shell expansion actually occurs.

Yes. Different shells have different rules about brace expansion in relation to variable expansion. Your way works in ksh, but not in zsh or bash. {1..$n} works in ksh and zsh but not in bash. In bash, variable expansion always happens after brace expansion.

The closest you'll get to this in bash is with eval.

that other guy
  • 116,971
  • 11
  • 170
  • 194
  • Good to know the differences between those shells indeed! – m-ric Feb 08 '13 at 22:49
  • Bash is completely unique in this regard. Even zsh performs brace (sequence) expansion just before globbing, as do all kshes that support it and csh (mksh has the same expansion order but doesn't support sequence expansion). Of course, there are major disadvantages to that too. Personally I prefer the Bash behaviour. – ormaaj Feb 23 '13 at 12:33
1

As long as the contents of the braces are literals, you can use brace expansion to populate an array with the full path names of the files to copy, then expand the contents of the array in your cp command.

$ FILES_TOOLS=( $HOME/tools/{fastboot,fastboot-HW.sh} )
$ cp "${FILES_TOOLS[@]}" $TOP_DIR/removeme

Update: I realized you might have a reason for having the base names alone in the variable. Here's another array-based solution that lets you prefix each element of the array with a path, again without an explicit loop:

$ FILES_TOOLS=( fastboot fastboot-HW.sh )
$ cp "${FILES_TOOLS[@]/#/$HOME/tools/}" $TOP_DIR/removeme

In this case, you use the pattern substitution operator to replace the empty string at the beginning of each array element with the directory name.

chepner
  • 497,756
  • 71
  • 530
  • 681
  • Too many brackets on a friday night... I will get a quite read of [this post](http://stackoverflow.com/questions/2188199/bash-double-or-single-bracket-parentheses-curly-braces). Thanks. – m-ric Feb 09 '13 at 00:55