10

I want to do something like this in a bash script. I'm using bash 4.1.10.

# rm -rf /some/path/{folder1,folder2,folder3}

Works nicely (and as expected) from the shell itself. It deletes the 3 desired folders leaving all others untouched.

When I put it into script something unwanted happens. For example, my script:

#!/bin/bash
set -x
VAR="folder1,folder2,folder3"
rm -rf /some/path/{$VAR}

When I execute this script, the folders are not deleted.

I think this is due to the fact that some unwanted quoting is occurring. Output from the script using #!/bin/bash -x:

rm -rf '/some/path/{folder1,folder2,folder3}'

which of course cannot succeed due to the ' marks.

How can I get this working within my script?

simont
  • 68,704
  • 18
  • 117
  • 136
Roland
  • 125
  • 1
  • 6
  • 1
    Do you absolutly want to use {}-expansion, or is an equivalent alternative ok? – carlpett Jul 01 '11 at 14:09
  • sure.. I just have a list of subfolder names I want to remove in ONE concrete subshell in background -means more concrete: rm -rf /some/path/{folder1,folder2,folder3} & not a loop which runs over each subfolder doing this as this would increase fileserver load – Roland Jul 01 '11 at 14:28

9 Answers9

12

According to the man page:

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.

So to get around this, add another level of expansion:

eval "rm -rf /some/path/{$VAR}"
Michael Lowman
  • 3,000
  • 1
  • 20
  • 34
5

Since you're writing a script, there's no reason to write hard-to-maintain code using eval tricks

VAR="f1,f2,f3"
IFS=,
set -- $VAR
for f; do
  rm -r "/path/to/$f"
done

or

VAR=( f1 f2 f3 )
for f in "${VAR[@]}"; do 
  rm -r "/path/to/$f"
done
glenn jackman
  • 238,783
  • 38
  • 220
  • 352
2

If your code can be rearranged, you can use echo and command substitution in bash. Something like this:

#!/bin/bash
set -x
VAR=`echo /some/path/{folder1,folder2,folder3}`
rm -rf $VAR
Jice
  • 51
  • 4
2

No, it's due to the fact that brace expansion happens before parameter expansion. Find another way of doing this, such as with xargs.

xargs -d , -I {} rm -rf /some/path/{} <<< "$VAR"
Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
1

You need to enable braceexpand flag:

#!/bin/bash
set -B
for i in /some/path/{folder1,folder2,folder3}
do
  rm -rf "$i"
done
user1338062
  • 11,939
  • 3
  • 73
  • 67
  • DOesn't seem to work for me. (Note that the `"/some/path/{folder1,folder2,folder3}"` string is stored in a variable; if you enter the above literally in a shell it will work because the shell does the expansion, but it's of no use in a script.) – ShreevatsaR Feb 07 '13 at 14:48
  • But if you know the actual literal list of things you want to do brace expansion of, then it would work: `for i in ${prefix}{fixed,list,of,literal,strings}${suffix}` would loop over 5 strings, with `${prefix}` and `${suffix}` properly substituted. – ShreevatsaR Feb 07 '13 at 15:02
  • Fixed the answer. You need to set -B / braceexpand with set, or provide it as a bash parameter :) – user1338062 Feb 07 '13 at 15:20
  • No, still doesn't work. I don't think you understand the question: suppose you have a variable, like `VAR="/tmp/{folder1,folder2,folder3}"`. Now you want to create (say) the corresponding directories, using $VAR. How do you do it? (If you don't have it in a var and can just type it directly, then everything is fine, as I said in my second comment above.) – ShreevatsaR Feb 07 '13 at 16:44
0

Another trick you can use (instead of the dangerous eval) is just plain echo inside a subshell. This works, for instance:

paths=`echo /some/path/{folder1,folder2,folder3}`
echo rm -rf $paths

outputs:

rm -rf /some/path/folder1 /some/path/folder2 /some/path/folder3

as desired. (Remove the "echo" in the second line, to make it actually do the rm.)


The crucial point is that bash does brace expansion before parameter expansion, so you never want to put a comma-separated list (surrounded by braces or not) into a variable -- if you do, then you'll have to resort to eval. You can however put a list of space-separated strings into a variable, by having the brace expansion happen in a subshell before assignment.

ShreevatsaR
  • 38,402
  • 17
  • 102
  • 126
0
#!/bin/bash
set -x
VAR="folder1,folder2,folder3"
eval "rm -rf /some/path/{$VAR}"

Edit The remainder is just for info. I try to be informative, but not wordy :_)

Recent bashes have the globstar option. This might perhaps come in handy in the future

shopt -s globstar
rm -rfvi some/**/folder?
sehe
  • 374,641
  • 47
  • 450
  • 633
  • i my concrete case I do not have folder1,folder2,folder3 but complete different names. Put for this special example this would have worked. – Roland Jul 01 '11 at 14:24
  • @Roland: I'm really mentioning `globstar` which I guess you didn't know yet. You did notice the answer (you know, the _first part_)? – sehe Jul 01 '11 at 14:26
0

The problem is not that in script mode some unwanted quoting is happening but that you put the folder names into a variable and the variable content is expanded after the brace expansion is done.
If you really want to do it like this you have to use eval:

eval "rm -rf /some/path/{$VAR}"
bmk
  • 13,849
  • 5
  • 37
  • 46
-1

replace {$VAR} by ${VAR} :-)

cadrian
  • 7,332
  • 2
  • 33
  • 42