Bash: expansion of braces in scripts that do not work due to unwanted escaping

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

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

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

When I put it in a 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, folders are not deleted.

I think this is due to some unwanted quotation happening. Output from script using #!/bin/bash -x :

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

which, of course, cannot be successful because of the marks. '

How can I make this work in my script?

+8
bash escaping brace-expansion
source share
9 answers

According to the man page :

The order of extensions: extension extensions, tilde extension, parameter, variable and arithmetic expansion and command substitution (performed from left to right), word splitting and path extension.

To get around this, add another extension level:

 eval "rm -rf /some/path/{$VAR}" 
+8
source share

Since you are writing a script, there is no reason to write hard 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 
+5
source share

No, this is due to the fact that the extension of the extensions occurs before the extension of the parameter. Find another way to do this, for example with xargs .

 xargs -d , -I {} rm -rf /some/path/{} <<< "$VAR" 
+2
source share

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

 #!/bin/bash set -x VAR=`echo /some/path/{folder1,folder2,folder3}` rm -rf $VAR 
+2
source share

You need to enable the braceexpand flag:

 #!/bin/bash set -B for i in /some/path/{folder1,folder2,folder3} do rm -rf "$i" done 
+1
source share
 #!/bin/bash set -x VAR="folder1,folder2,folder3" eval "rm -rf /some/path/{$VAR}" 

Change The remaining information is for information only. I try to be informative, but not verbose: _)

Recent changes have the globstar option. It may be useful in the future.

 shopt -s globstar rm -rfvi some/**/folder? 
0
source share

The problem is not that in script mode unwanted quoting occurs, but you put the folder names in the variable, and the contents of the variable expand after the curly brace extension completes.
If you really want to do this, you need to use eval :

 eval "rm -rf /some/path/{$VAR}" 
0
source share

Another trick you can use (instead of the dangerous eval ) is simply echo inside the subshell. This works for example:

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

outputs:

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

optional. (Remove the echo in the second line to actually make rm .)


The most important point is that bash supports expansion before parameter expansion, so you never want to put a comma-delimited list (surrounded by curly braces or not) in a variable - if you do, then you will have to resort to eval . However, you can put a list of lines separated by spaces in a variable by expanding the brace extension in a subshell before assigning it.

0
source share

replace {$VAR} with ${VAR}

-one
source share

All Articles