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

时间:2009-04-26 03:41:46

标签: linux bash shell

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

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

6 个答案:

答案 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上为我工作。