繁体   English   中英

rm无法通过脚本中的通配符删除文件,但是可以在shell提示符下工作

[英]rm fails to delete files by wildcard from a script, but works from a shell prompt

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"

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

TL; DR

仅引用变量,而不引用通配符的整个预期路径

rm "$archivedir"/*.bz2

说明

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

  • 您可以通过用双引号,单引号或通配符将通配符引起来来禁用此过程。 但是,这不是您想要的-您确实希望通配符扩展到它匹配的文件列表。

  • 编写rm $archivedir/*.bz2 (不带引号)时要小心。 $ archivedir被替换后,发生单词split(即,将命令行分解为参数)。 因此,如果$ 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

你原来的台词

rm "$archivedir/*.bz2"

可以重写为

rm "$archivedir"/*.bz2

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

为了进一步扩展,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` 

(要以这种方式打印所需的内容,这需要几次尝试 ,显然是不可能的……)

引号导致字符串被解释为字符串文字,请尝试将其删除。

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM