将globbing的结果分配给Bash中的变量

Rob*_*her 28 bash glob

我的同事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
Run Code Online (Sandbox Code Playgroud)

由此我推断,在echo命令期间发生了globbing,而不是在创建变量FOO时.

我们有一些解决方法,按照不正常的降序排列:

touch `echo $FOO`/rotten_eggs
Run Code Online (Sandbox Code Playgroud)

要么:

pushd
cd $FOO
touch rotten_eggs
popd
Run Code Online (Sandbox Code Playgroud)

但两者都不令人满意.我错过了一招吗?

jor*_*anm 36

问题是如果文件"rotten_eggs"存在,glob只会扩展,因为它包含在glob模式中.你应该使用一个数组.

FOO=( ryan/smells-* )
touch "${FOO[@]/%//rotten_eggs}"
Run Code Online (Sandbox Code Playgroud)

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

  • 这根本不是问题。问题是 bash 在扩展之前将 glob 分配给变量。即使文件存在,也会为变量分配一个 glob,而不是文件名;并且该 glob 将在使用时扩展 (2认同)
  • @jordanm 但与所问的问题和标题直接相关。实际上,回声中发生了通配符。我会添加一个答案而不是争论它,因为你的观点很好,你的答案很有用。 (2认同)
  • @SamLiddicott 是非常正确的,但即使在他的完整答案中也可能不够清楚。在变量赋值“name=[value]”中,值会经历波形符扩展、参数和变量扩展、命令替换、算术扩展和引号删除,但不会进行路径扩展(通配符等)。尝试 `FOO=ryan/smells-*; 回显“$FOO”;echo $FOO;` 查看实际分配给问题的 FOO 的内容。 (2认同)

jil*_*les 7

考虑

for dir in $FOO; do
    touch "$dir/rotten_eggs"
done
Run Code Online (Sandbox Code Playgroud)

请注意,touch如果glob模式匹配多个路径名,则会有多个文件.

  • 根据具体情况,这可以说更清楚. (2认同)

Sam*_*ott 5

预期的代码与分配给变量的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
Run Code Online (Sandbox Code Playgroud)

$FOO 实际上这里是一个数组,但$ FOO也可以获得数组的第一个元素.

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

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

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

当然这意味着变量应该总是在双引号中使用,"..."否则如果文件名本身(glob扩展)中也有一个*,它会再次变为glob.

例如

$ touch ryan/'*ea*'
$ FOO=(ryan/*ea*)
$ echo "${FOO[@]}"
ryan/clean ryan/*ea*
$ echo ${FOO[@]}
ryan/clean ryan/clean ryan/*ea*
Run Code Online (Sandbox Code Playgroud)