bash rm 无法通过通配符从脚本中删除文件,但可以在 shell 提示符下工作
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/790195/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
rm fails to delete files by wildcard from a script, but works from a shell prompt
提问by EMP
I've run into a really silly problem with a Linux shell script. I want to delete all files with the extension ".bz2" in a directory. In the script I call
我在使用 Linux shell 脚本时遇到了一个非常愚蠢的问题。我想删除目录中所有扩展名为“.bz2”的文件。在我调用的脚本中
rm "$archivedir/*.bz2"
where $archivedir is a directory path. Should be pretty simple, shouldn't it? Somehow, it manages to fail with this error:
其中 $archivedir 是目录路径。应该很简单,不是吗?不知何故,它设法失败并出现此错误:
rm: cannot remove `/var/archives/monthly/April/*.bz2': No such file or directory
But there isa file in that directory called test.bz2 and if I change my script to
但是,是在该目录名为test.bz2一个文件,如果我改变我的脚本
echo rm "$archivedir/*.bz2"
and copy/paste the output of that line into a terminal window the file is removed successfully. What am I doing wrong?
并将该行的输出复制/粘贴到终端窗口中,文件已成功删除。我究竟做错了什么?
回答by Doug
TL;DR
TL; 博士
Quote only the variable, not the whole expected path with the wildcard
仅引用变量,而不是使用通配符引用整个预期路径
rm "$archivedir"/*.bz2
Explanation
解释
In Unix, programs generally do not interpret wildcards themselves. The shell interprets unquoted wildcards, and replaces each wildcard argument with a list of matching file names. if $archivedir might contain spaces, then
rm $archivedir/*.bz2
might not do what youYou can disable this process by quoting the wildcard character, using double or single quotes, or a backslash before it. However, that's not what you want here - you do want the wildcard expanded to the list of files that it matches.
Be careful about writing
rm $archivedir/*.bz2
(without quotes). The word splitting (i.e., breaking the command line up into arguments) happens after$archivedir is substituted. So if $archivedir contains spaces, then you'll get extra arguments that you weren't intending. Say archivedir is/var/archives/monthly/April to June
. Then you'll get the equivalent of writingrm /var/archives/monthly/April to June/*.bz2
, which tries to delete the files "/var/archives/monthly/April", "to", and all files matching "June/*.bz2", which isn't what you want.
在 Unix 中,程序本身通常不解释通配符。shell 解释不带引号的通配符,并用匹配文件名列表替换每个通配符参数。如果 $archivedir 可能包含空格,则
rm $archivedir/*.bz2
可能不会执行您的操作您可以通过引用通配符、使用双引号或单引号或在它之前使用反斜杠来禁用此过程。但是,这不是您想要的 - 您确实希望将通配符扩展到它匹配的文件列表。
书写时要小心
rm $archivedir/*.bz2
(不带引号)。单词拆分(即,将命令行分解为参数)发生在$archivedir 被替换之后。因此,如果 $archivedir 包含空格,那么您将获得您不想要的额外参数。说 archivedir 是/var/archives/monthly/April to June
. 然后你会得到相当于writingrm /var/archives/monthly/April to June/*.bz2
,它试图删除文件“/var/archives/monthly/April”、“to”和所有匹配“June/*.bz2”的文件,这不是你想要的.
The correct solution is to write:
正确的解决方法是这样写:
rm "$archivedir"/*.bz2
回答by Avery Payne
Your original line
你原来的线路
rm "$archivedir/*.bz2"
Can be re-written as
可以重写为
rm "$archivedir"/*.bz2
to achieve the same effect. The wildcard expansion is not taking place properly in your existing setup. By shifting the double-quote to the "front" of the file path (which is legitimate) you avoid this.
达到同样的效果。通配符扩展在您现有的设置中没有正确进行。通过将双引号移到文件路径的“前面”(这是合法的),您可以避免这种情况。
回答by Charlie Martin
Just to expand on this a bit, bash has fairly complicated rules for dealing with metacharacters in quotes. In general
只是稍微扩展一下,bash 有相当复杂的规则来处理引号中的元字符。一般来说
almost nothing is interpreted in single-quotes:
echo '$foo/*.c' => $foo/*.c echo '\*' => \*
shell substitution is done inside double quotes, but file metacharacters aren't expanded:
FOO=hello; echo "$foo/*.c" => hello/*.c
everything inside backquotes is passed to the subshell which interprets them. A shell variable that is not exported doesn't get defined in the subshell. So, the first command echoes blank, but the second and third echo "bye":
BAR=bye echo `echo $BAR` BAR=bye; echo `echo $BAR` export BAR=bye; echo `echo $BAR`
几乎没有什么是用单引号解释的:
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`
(And getting this to print the way you want it in SO takes several triesis apparently impossible...)
(并且让它以您想要的方式打印,所以需要多次尝试显然是不可能的......)
回答by Brian Gianforcaro
The quotes are causing the string to be interpreted as a string literal, try removing them.
引号导致字符串被解释为字符串文字,请尝试删除它们。
回答by Christo
I've seen similar errors when calling a shell script like ./shell_script.shfrom another shell script. This can be fixed by invoking it as sh shell_script.sh
从另一个 shell 脚本调用./shell_script.sh这样的 shell 脚本时,我看到过类似的错误 。这可以通过将其调用为 sh shell_script.sh 来修复
回答by holms
Why not just rm -rf */*.bz2
? Works for me on OSX.
为什么不只是rm -rf */*.bz2
?在 OSX 上对我有用。