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

我用Linux shell脚本遇到了一个非常愚蠢的问题。 我想删除目录中所有扩展名为“.bz2”的文件。 在我打电话的剧本里

rm "$archivedir/*.bz2" 

其中$ archivedir是一个目录path。 应该很简单,不是吗? 不知怎的,它pipe理失败,这个错误:

 rm: cannot remove `/var/archives/monthly/April/*.bz2': No such file or directory 

但是在那个目录下有一个名为test.bz2的文件,如果我把脚本改成

 echo rm "$archivedir/*.bz2" 

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

TL; DR

只引用variables,而不是通配符的整个预期path

 rm "$archivedir"/*.bz2 

说明

  • 在Unix中,程序通常不会自行解释通配符。 shell解释未加引号的通配符,并用匹配的文件名列表replace每个通配符参数。 如果$ archivedir可能包含空格,那么rm $archivedir/*.bz2可能不会执行你的操作

  • 您可以通过引用通配符来禁用此过程,使用双引号或单引号,或者在其之前使用反斜杠。 但是,这不是你想要的 – 你想要通配符扩展到它匹配的文件列表。

  • 注意写rm $archivedir/*.bz2 (不含引号)。 分割这个词(即将命令行分解为参数) $ archivedir被replace之后发生。 所以如果$ archivedir包含空格,那么你会得到额外的参数,你不打算。 说archivedir是/var/archives/monthly/April to June 。 然后,您将得到相当于写入rm /var/archives/monthly/April to June/*.bz2 ,它试图删除文件“/ var / archives / monthly / April”,“to”以及所有匹配的文件“六月/ *。bz2”,这不是你想要的。

正确的解决办法是写:

  rm“$ archivedir”/ *。bz2 

只是为了扩大这一点,bash有相当复杂的处理引号中的元字符的规则。 一般来说

  • 几乎没有什么是用单引号解释的:

      echo '$foo/*.c' => $foo/*.c echo '\\*' => \\* 
  • shellreplace在双引号内完成,但文件元字符不扩展:

      FOO=hello; echo "$foo/*.c" => hello/*.c 
  • 反引号内的所有内容都被传递给解释它们的子shell。 未导出的shellvariables未在子shell中定义。 所以,第一个命令回声空白,但第二个和第三个回声“再见”:

     BAR=bye echo `echo $BAR` BAR=bye; echo `echo $BAR` export BAR=bye; echo `echo $BAR` 

(让这打印你想要的方式在这需要几次尝试显然是不可能的…)

你的原始线

 rm "$archivedir/*.bz2" 

可以重写为

 rm "$archivedir"/*.bz2 

达到同样的效果。 通配符扩展在您现有的设置中没有正确发生。 通过将双引号移到文件path的“前面”(这是合法的),可以避免这种情况。

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

从另一个shell脚本调用像./shell_script.sh这样的shell脚本时,我看到了类似的错误。 这可以通过调用sh shell_script.sh来解决

有关内部工作的更多信息:请尝试input:

 echo "$archivedir/*.bz2" 

在shell提示符下。 你会看到它立即展开。 所以根本就不会看到* 相反,这是传递给它的列表。

编辑:我看到你基本上试过。 那么你想做什么,是在该目录中有几个bz2文件时尝试。 然后你会看到效果。