rm无法通过通配符从脚本中删除文件,但是从shell提示符开始工作

问题描述 投票:54回答:6

我遇到了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"

并将该行的输出复制/粘贴到终端窗口中,成功删除该文件。我究竟做错了什么?

linux bash shell
6个回答
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

10
投票

你原来的路线

rm "$archivedir/*.bz2"

可以重写为

rm "$archivedir"/*.bz2

达到同样的效果。通配符扩展在现有设置中无法正常进行。通过将双引号转移到文件路径的“前端”(这是合法的),您可以避免这种情况。


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`

(并让它以你想要的方式打印出来 需要多次尝试 显然是不可能的...)


4
投票

引号导致字符串被解释为字符串文字,尝试删除它们。


0
投票

从另一个shell脚本调用类似./shell_script.sh的shell脚本时,我发现了类似的错误。这可以通过调用sh shell_script.sh来修复


0
投票

为什么不只是rm -rf */*.bz2?在OSX上为我工作。

© www.soinside.com 2019 - 2024. All rights reserved.