我遇到了一个Linux shell脚本非常愚蠢的问题。我想在目录中删除扩展名为“.bz2”的所有文件。在脚本中我打电话
rm "$archivedir/*.bz2"
其中$ archivedir是目录路径。应该很简单,不应该吗?不知何故,它设法失败并出现此错误:
rm: cannot remove `/var/archives/monthly/April/*.bz2': No such file or directory
但是该目录中名为test.bz2的文件,如果我将脚本更改为
echo rm "$archivedir/*.bz2"
并将该行的输出复制/粘贴到终端窗口中,成功删除该文件。我做错了什么?
答案 0 :(得分:100)
<强> TL; DR 强>
仅引用变量,而不是通配符
的整个预期路径rm "$archivedir"/*.bz2
<强>解释强>
在Unix中,程序通常不会自己解释通配符。 shell解释不带引号的通配符,并用匹配文件名列表替换每个通配符参数。
如果$ archivedir可能包含空格,那么rm $archivedir/*.bz2
可能无法执行您的
您可以通过引用通配符,使用双引号或单引号或前面的反斜杠来禁用此过程。但是,这不是你想要的 - 你确实希望通配符扩展到它匹配的文件列表。
小心写rm $archivedir/*.bz2
(不带引号)。在 $ archivedir被替换之后,单词拆分(即,将命令行分解为参数)发生。因此,如果$ archivedir包含空格,那么您将获得您不想要的额外参数。说archivedir是/var/archives/monthly/April to June
。然后你会得到相当于写rm /var/archives/monthly/April to June/*.bz2
,它试图删除文件“/ var / archives / monthly / April”,“to”,以及所有匹配“June / * .bz2”的文件,这是不管你想要什么。
正确的解决方案是写:
rm "$archivedir"/*.bz2
答案 1 :(得分:10)
你原来的行
rm "$archivedir/*.bz2"
可以重写为
rm "$archivedir"/*.bz2
达到同样的效果。通配符扩展在现有设置中无法正常进行。通过将双引号转移到文件路径的“前端”(这是合法的),可以避免这种情况。
答案 2 :(得分:9)
为了扩展这一点,bash在处理引号中的元字符时有相当复杂的规则。一般来说
几乎没有任何单引号解释:
echo '$foo/*.c' => $foo/*.c
echo '\\*' => \\*
shell替换在双引号内完成,但不扩展文件元字符:
FOO=hello; echo "$foo/*.c" => hello/*.c
反引号内的所有内容都会传递给解释它们的子shell。未导出的shell变量未在子shell中定义。所以,第一个命令回显空白,但第二个和第三个回声“再见”:
BAR=bye echo `echo $BAR`
BAR=bye; echo `echo $BAR`
export BAR=bye; echo `echo $BAR`
(并且在SO 中按照你想要的方式打印这需要几次尝试显然是不可能的......)
答案 3 :(得分:4)
引号导致字符串被解释为字符串文字,请尝试删除它们。
答案 4 :(得分:0)
我在调用shell脚本时遇到过类似的错误 的 ./ shell_script.sh 强> 来自另一个shell脚本。这可以通过调用它来修复 sh shell_script.sh
答案 5 :(得分:0)
为什么不只是rm -rf */*.bz2
?在OSX上为我工作。