Bash:由于不需要的转义,脚本中的大括号扩展不起作用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6549037/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 00:16:48  来源:igfitidea点击:

Bash: Brace expansion in scripts not working due to unwanted escaping

bashescapingbrace-expansion

提问by Roland

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

我想在 bash 脚本中做这样的事情。我正在使用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.

从 shell 本身可以很好地工作(并且正如预期的那样)。它会删除 3 个所需的文件夹,而其他所有文件夹均保持原样。

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:

我认为这是由于发生了一些不需要的引用。使用#!/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?

我怎样才能在我的脚本中使用它?

回答by Michael Lowman

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}"

回答by glenn Hymanman

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

由于您正在编写脚本,因此没有理由使用eval技巧编写难以维护的代码

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

回答by Jice

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

如果您的代码可以重新排列,您可以echo在 bash 中使用和命令替换。像这样的东西:

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

回答by Ignacio Vazquez-Abrams

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.

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

回答by user1338062

You need to enable braceexpand flag:

您需要启用 braceexpand 标志:

#!/bin/bash
set -B
for i in /some/path/{folder1,folder2,folder3}
do
  rm -rf "$i"
done

回答by ShreevatsaR

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

您可以使用的另一个技巧(而不是危险的eval)只是echo在子外壳中。这有效,例如:

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.)

如预期的。(删除第二行中的“回声”,使其实际执行rm。)



The crucial point is that bash does brace expansion beforeparameter 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 beforeassignment.

关键是 bash参数扩展之前进行大括号扩展,因此您永远不想将逗号分隔的列表(由大括号括起来与否)放入变量中——如果这样做,那么您将不得不求助于eval. 但是,您可以将空格分隔的字符串列表放入变量中,方法是赋值之前在子 shell 中进行大括号扩展。

回答by bmk

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

问题不在于在脚本模式下发生了一些不需要的引用,而是将文件夹名称放入变量中,并且在大括号扩展完成扩展了变量内容。
如果你真的想这样做,你必须使用eval

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

回答by sehe

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


EditThe 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

最近的 bash 有 globstar 选项。这可能在未来派上用场

shopt -s globstar
rm -rfvi some/**/folder?

回答by cadrian

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

替换{$VAR}${VAR}:-)