【问题标题】:Bash: Brace expansion in scripts not working due to unwanted escapingBash:由于不需要的转义,脚本中的大括号扩展不起作用
【发布时间】:2011-07-01 14:06:24
【问题描述】:

我想在 bash 脚本中执行类似的操作。我正在使用bash 4.1.10

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

从外壳本身很好地工作(并且如预期的那样)。它会删除 3 个所需的文件夹,而其他所有文件夹保持不变。

当我将它放入脚本时,会发生一些不想要的事情。比如我的脚本:

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

当我执行这个脚本时,文件夹没有被删除。

我认为这是由于发生了一些不需要的引用。使用#!/bin/bash -x从脚本输出:

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

由于' 标记,这当然不能成功。

如何在我的脚本中实现这个功能?

【问题讨论】:

  • 您是绝对要使用 {}-expansion,还是等效的替代方法?
  • 当然..我只有一个子文件夹名称列表,我想在后台的一个具体子shell中删除 - 意味着更具体:rm -rf /some/path/{folder1,folder2,folder3} &不是在每个子文件夹上运行的循环,因为这会增加文件服务器负载

标签: bash escaping brace-expansion


【解决方案1】:

根据the man page

扩展的顺序是:大括号扩展、波浪号扩展、参数、变量和算术扩展以及命令替换(以从左到右的方式完成)、分词和路径名扩展。

所以为了解决这个问题,添加另一个扩展级别:

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

【讨论】:

    【解决方案2】:

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

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

    VAR=( f1 f2 f3 )
    for f in "${VAR[@]}"; do 
      rm -r "/path/to/$f"
    done
    

    【讨论】:

      【解决方案3】:

      不,这是因为大括号扩展发生在参数扩展之前。找到另一种方法,例如使用xargs

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

      【讨论】:

        【解决方案4】:

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

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

        【讨论】:

          【解决方案5】:

          您需要启用大括号扩展标志:

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

          【讨论】:

          • 似乎对我不起作用。 (请注意,"/some/path/{folder1,folder2,folder3}" 字符串存储在一个变量中;如果您在 shell 中按字面意思输入上面的内容,它会起作用,因为 shell 会进行扩展,但在脚本中没有用。)
          • 但是,如果您知道要对其进行大括号扩展的实际文字列表,那么它将起作用:for i in ${prefix}{fixed,list,of,literal,strings}${suffix} 将循环超过 5 个字符串,${prefix}${suffix} 正确替换.
          • 修复了答案。您需要使用 set 设置 -B/braceexpand,或者将其作为 bash 参数提供:)
          • 不行,还是不行。我认为您不理解这个问题:假设您有一个变量,例如VAR="/tmp/{folder1,folder2,folder3}"。现在你想创建(比如说)相应的目录,使用 $VAR。你怎么做呢? (如果你没有它在 var 中并且可以直接输入它,那么一切都很好,正如我在上面的第二条评论中所说的那样。)
          【解决方案6】:

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

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

          【讨论】:

            【解决方案7】:
            #!/bin/bash
            set -x
            VAR="folder1,folder2,folder3"
            eval "rm -rf /some/path/{$VAR}"
            

            编辑其余部分仅供参考。我尽量提供信息,但不罗嗦:_)

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

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

            【讨论】:

            • 我的具体案例我没有文件夹 1、文件夹 2、文件夹 3,但名称不同。对于这个特殊的例子,这会起作用。
            • @Roland:我真的提到了globstar,我猜你还不知道。你确实注意到了答案(你知道,第一部分)?
            【解决方案8】:

            您可以使用的另一个技巧(而不是危险的eval)只是在子shell 中的普通echo。例如,这是可行的:

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

            输出:

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

            根据需要。 (删除第二行中的“echo”,使其实际执行rm。)


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

            【讨论】:

              【解决方案9】:

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

              【讨论】:

              • 我希望生成关于 $VAR 内容的大括号,而不是 $VAR 的大括号扩展
              猜你喜欢
              • 1970-01-01
              • 2012-02-07
              • 1970-01-01
              • 2021-03-07
              • 2017-03-03
              • 1970-01-01
              • 2019-02-01
              • 2016-01-10
              相关资源
              最近更新 更多