【问题标题】:Assign results of globbing to a variable in Bash将 globbing 的结果分配给 Bash 中的变量
【发布时间】:2012-03-03 01:05:45
【问题描述】:

我的同事 Ryan 向我提出了他的 Bash 脚本中的一个错误,我发现了这个测试的问题:

$ mkdir ryan
$ mkdir ryan/smells-bad
$ FOO=ryan/smells-*
$ echo $FOO
ryan/smells-bad
$ touch $FOO/rotten_eggs
touch: cannot touch `ryan/smells-*/rotten_eggs': No such file or directory

由此我推断,通配是在 echo 命令期间发生的,而不是在创建变量 FOO 时发生的。

我们有几个变通方法,按不优雅的降序排列:

touch `echo $FOO`/rotten_eggs

或者:

pushd
cd $FOO
touch rotten_eggs
popd

但两者都不令人满意。我错过了一个技巧吗?

【问题讨论】:

    标签: bash glob


    【解决方案1】:

    考虑

    for dir in $FOO; do
        touch "$dir/rotten_eggs"
    done
    

    请注意,如果 glob 模式匹配多个路径名,这将touch 多个文件。

    【讨论】:

    • 这可行,但它需要触摸运行多次​​,而不是一次。
    【解决方案2】:

    问题在于,只有当文件“rotten_eggs”存在时,glob 才会扩展,因为它包含在 glob 模式中。你应该使用一个数组。

    FOO=( ryan/smells-* )
    touch "${FOO[@]/%//rotten_eggs}"
    

    FOO 数组包含与 glob 匹配的所有内容。使用 % 的扩展将 /rotten_eggs 附加到每个元素。

    【讨论】:

    • 谢谢,这很好地解释了它。
    • 这根本不是问题。问题是 bash 在扩展之前将 glob 分配给变量。即使文件存在,也会为变量分配一个 glob,而不是文件名;并且该 glob 将在使用时扩展
    • @SamLiddicott 如果存在“rotten_eggs”,OP 的示例会起作用,因此或多或少是个问题。 glob 的扩展时间与 OP 的简短示例无关。
    • @jordanm 但与所提出的问题和标题直接相关。实际上,回声中发生了globbing。我会添加一个答案而不是争论它,因为你的观点很好,你的答案很有用。
    • @SamLiddicott 是非常正确的,但即使在他的完整答案中也可能不够清楚。在变量赋值name=[value] 中,值经历波浪号扩展、参数和变量扩展、命令替换、算术扩展和引号删除,但不是路径扩展(通配符等)。尝试FOO=ryan/smells-*; echo "$FOO"; echo $FOO; 看看真正分配给问题的 FOO 的内容。
    【解决方案3】:

    我会这样做:

    for FOO in ryan/smells-*; do
      touch "$FOO"/rotten_eggs
    done
    

    这样$FOO 包含实际的目录名称,而不是全局模式。但是,如果有多个匹配项,它将只包含循环之后的最后一个匹配项,因此数组解决方案可能更适合这种情况。

    【讨论】:

      【解决方案4】:

      将 glob 的结果分配给变量的预期代码如下:

      $ mkdir ryan
      $ mkdir ryan/smells-bad
      $ FOO=(ryan/smells-*)
      $ echo "${FOO[@]}"
      ryan/smells-bad
      $ echo "$FOO"
      ryan/smells-bad
      $ touch "$FOO/rotten_eggs"
      $ ls -l "$FOO"
      total 0
      -rw-r--r-- 1 ryan ryan 0 Mar  1 11:17 rotten_eggs
      

      $FOO 这里其实是一个数组,但是 $FOO 也可以用来获取数组的第一个元素。

      但是,看看 glob 如何匹配多个文件(因此数组是个好主意)

      $ mkdir ryan/clean
      $ FOO=(ryan/*)
      $ echo "$FOO"
      ryan/clean
      $ echo "${FOO[@]}"
      ryan/clean ryan/smells-bad
      

      在这些情况下,glob 的结果会根据需要分配给变量,而不是在使用时将变量扩展为 glob。

      当然,这意味着该变量应该始终使用双引号 "...",否则如果文件名本身(glob 扩展)也包含 *,它将再次 glob。

      例如

      $ touch ryan/'*ea*'
      $ FOO=(ryan/*ea*)
      $ echo "${FOO[@]}"
      ryan/clean ryan/*ea*
      $ echo ${FOO[@]}
      ryan/clean ryan/clean ryan/*ea*
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-01-23
        • 2012-10-13
        • 2013-12-06
        • 2021-02-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多