我的同事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命令期间发生了globbing,而不是在创建变量FOO时。
我们有几个解决方法,按照不正常的降序排列:
touch `echo $FOO`/rotten_eggs
或者:
pushd
cd $FOO
touch rotten_eggs
popd
但两者都不令人满意。我错过了一招吗?
答案 0 :(得分:34)
问题是如果文件“rotten_eggs”存在,glob只会扩展,因为它包含在glob模式中。你应该使用一个数组。
FOO=( ryan/smells-* )
touch "${FOO[@]/%//rotten_eggs}"
FOO数组包含glob匹配的所有内容。使用%的扩展会将/ rotten_eggs附加到每个元素。
答案 1 :(得分:7)
考虑
for dir in $FOO; do
touch "$dir/rotten_eggs"
done
请注意,如果glob模式匹配多个路径名,这将touch
多个文件。
答案 2 :(得分: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
$FOO
实际上是一个数组,但$ FOO也可用于获取数组的第一个元素。
但是,看看glob如何匹配多个文件(因此数组是一个好主意)
$ mkdir ryan/clean
$ FOO=(ryan/*)
$ echo "$FOO"
ryan/clean
$ echo "${FOO[@]}"
ryan/clean ryan/smells-bad
在这些情况下,glob的结果会根据需要分配给变量,而不是在使用时将变量扩展为glob。
当然这意味着变量应该总是在双引号"..."
中使用,否则如果文件名本身(glob扩展)中也有*
,它会再次变为glob。
e.g。
$ touch ryan/'*ea*'
$ FOO=(ryan/*ea*)
$ echo "${FOO[@]}"
ryan/clean ryan/*ea*
$ echo ${FOO[@]}
ryan/clean ryan/clean ryan/*ea*
答案 3 :(得分:3)
我会这样做:
for FOO in ryan/smells-*; do
touch "$FOO"/rotten_eggs
done
这种方式$FOO
包含实际的目录名,而不是glob模式。但是,如果有多个匹配,它将只包含循环后的最后一个匹配,因此对于这种情况,阵列解决方案可能更好。