2

Consider the following script:

#! /bin/bash -e

echo {foo,bar}
EX={foo,bar}
echo ${EX}

The output of this script is:

foo bar
{foo,bar}

I would like the the echo command to perform brace expansion on ${EX}. Thus, I would like to see an output of

foo bar
foo bar

I want to create a script where the user can supply a path with curly brackets where every expanded version of it is copied.

Something like this:

#! /bin/bash -e

$SOURCES=$1
$TARGET=$2

cp -r ${SOURCES} ${TARGET}

How can I achieve this?

Jahid
  • 21,542
  • 10
  • 90
  • 108
Johannes Dorn
  • 1,307
  • 1
  • 16
  • 34
  • 1
    What is `cp -r foo bar foo bar` supposed to do? – anubhava May 17 '16 at 08:31
  • 2
    Also you can't do brace expansion with variables, as it is evaluated before them. – 123 May 17 '16 at 08:34
  • don't make things over-complicated. You write your script once and then use it numerous times (probably). So, there is no reason to try to save on that. – Serge May 17 '16 at 08:36

3 Answers3

8

This is a way:

ex=({foo,bar,baz})
echo ${ex[@]}
foo bar baz
Jahid
  • 21,542
  • 10
  • 90
  • 108
1

See man bash:

The order of expansions is: brace expansion, tilde expansion, parameter, variable and arithmetic expansion and command substitution (done in a left-to-right fashion), word splitting, and pathname expansion.

As you see, variable expansion happens later than brace expansion.

Fortunately, you don't need it at all: let the user specify the braced paths, let the shell expand them. You can then just

mv "$@"

If you need to separate the arguments, use an array and parameter expansion:

sources=("${@:1:$#-1}")
target=${@: -1}
mv "${sources[@]}" "$target"
choroba
  • 231,213
  • 25
  • 204
  • 289
  • Good advice - if you accept already expanded arguments, then your shell script behaves more like other applications, and the user gets to use their shell in the normal way (rather than having to quote arguments that are to be globbed by the script). – Toby Speight May 17 '16 at 09:14
  • Why `mv "$@"` brace expand works when it is in the script? What bash construct make this possible. And how it's different from question example? (which doesn't work). – mauron85 Mar 04 '19 at 21:33
  • @mauron85: because the shell expands the unquoted brace expression and your scripts gets a list of the arguments as parameters. – choroba Mar 04 '19 at 23:28
1

Brace expansion does not work the way you are attempting to use it. Brace expansion is basically used to generate lists to be applied within the context of the present command. You have two primary modes where brace expansion is used directly (and many more where brace expansion is used a part of another operator.) The two direct uses are to expand a list of items within a comma-separated pair of braces. e.g.

$ touch file_{a,b,c,d}.txt

After executing the command, brace expansion creates all four files with properly formatted file names in the present directory:

$ ls -1 file*.txt
file_a.txt
file_b.txt
file_c.txt
file_d.txt

You may also use brace-expansion in a similar manner to generate lists for loop iteration (or wherever a generated system/range of numbers in needed). The syntax for using brace expansion here is similar, but with .. delimiters within the braces (instead of ',' separation). The syntax is {begin..end..increment} (whereincrement can be both positive and negative) e.g.

$ for i in {20..-20..-4}; do echo $i; done)
20
16
12
8
4
0
-4
-8
-12
-16
-20

(note: using variables for begin, end or increment is not allowed without some horrible eval trickery -- avoid it.).

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85