使用zsh
,当选择不适合的模式时rm
,即使重定向输出,我也会收到“未找到匹配”消息。
# rm * > /dev/zero 2>&1
zsh: no matches found: *
我怎样才能摆脱这个消息?
答案1
此行为由多个控制Zsh 的通配选项。默认情况下,如果命令行包含与任何内容都不匹配的通配表达式,Zsh 将打印您看到的错误消息,并且根本不运行该命令。您可以通过三种不同的方式禁用此功能:
setopt +o nomatch
将留下与任何内容不匹配的通配表达式,并且您将收到一条错误消息
rm
(您可以禁用 using-f
,尽管这是一个坏主意,因为它会在您可能不希望的其他情况下强制删除) ;setopt +o nullglob
将删除与任何内容都不匹配的模式(因此它们将被有效地忽略);
setopt +o cshnullglob
将删除与任何内容都不匹配的模式,如果命令中的所有模式都被删除,则报告错误。
最后两个覆盖nomatch
。所有这些选项都可以通过 取消设置setopt -o …
。
nullglob
可以使用启用单个模式全局N
限定符,例如:
rm -f -- *(N)
答案2
您希望它做什么?rm
根本没有运行(1)?*
像其他类似 Bourne 的 shell (2) 一样使用文字参数运行它?完全不带任何参数运行它 (3)?
files=(*(N)); (($#files)) && rm -- $files
。或者(rm -- *) 2> /dev/null
,但这也会隐藏真正的错误,rm
这是愚蠢的。您可以丢弃错误,但恢复命令zsh
的 stderrrm
(rm -- * 2>&3 3>&-) 3>&2 2> /dev/null
emulate sh -c 'rm -- *' 2> /dev/null
。然后,就像现在为该单个命令行模拟的sh
那样zsh
,不匹配的内容*
按原样传递到rm
并rm
抱怨该*
文件不存在。我们抑制rm
的 stderr ,就像您sh
抑制该错误消息一样,但同样,这很愚蠢,因为它会隐藏真正的错误,rm
而不是通过将sh
文字传递*
给 的不当行为而产生的错误rm
。不过,rm -f '*'
不会抱怨不存在的*
文件,所以你可以这样做emulate sh -c 'rm -f -- *'
rm -- *(N)
。rm
虽然当没有通过任何参数时会抱怨,但同样,不是rm -f
:rm -f -- *(N)
。
一般来说,rm -f
如果您希望所有文件都消失,并且只有在文件无法删除或rm
返回后 IOW 仍然存在时才会出现错误,则需要使用该命令。您通常还希望-f
在脚本中使用以避免在某些情况下提示用户。
这里,rm
当 glob 不匹配时调用是错误的。1 行为是错误的。对于像 这样的模式,这是无害的,但对于像 这样的模式,当sh
它不匹配时按原样传递可能会导致文件被错误删除:*
*.[ch]
*.[ch]
*.[ch]
$ ls
*.[ch] foo.txt
$ zsh -c 'rm *.[ch]'
zsh:1: no matches found: *.[ch]
$ ls
*.[ch] foo.txt
$ sh -c 'rm *.[ch]'
$ ls
foo.txt
因错误而失败是最明智的做法,也是zsh
(以及fish
、csh
、tcsh
和bash -o failglob
原始 Unix shell)所做的。
如果您想自己处理这种特殊情况,zsh
可以使用它来轻松完成(N)
全局限定符(为了空球) 就像上面的情况 (1) 一样。fish
(至少在最新版本)使得它变得更加容易,因为它的全局在一个空球在命令的参数中set
(分配变量的命令)时的时尚。因此,等效的情况是:
set files *
if count $files > /dev/null
rm -f -- $files
end
看为什么 nullglob 不是默认值更多细节。
1.严格来说,它只是sh
自 Bourne shell 以来(自 1979 年的 Unix V7 以来);早期版本sh
(确实调用了/etc/glob
未加引号的通配符,这就是全局name 来自)确实表现得像csh
or zsh -o cshnullglob
,也就是说,/etc/glob
如果没有一个 glob 具有任何匹配项,则会中止该命令(并且如果至少有一个 glob 具有任何匹配项,则会抑制不匹配的 glob)。该行为是破碎的通过 Bourne shell。
答案3
在交互式 zsh 中输入:
setopt no_nomatch
rm * > /dev/null 2>&1
setopt nomatch # `nomatch` is default, see `man zshoptions`
对我来说,nullglob
不cshnullglob
工作
答案4
由于缺乏声誉,我无法发表评论。作为对斯蒂芬·基特答案的补充,我必须使用setopt
而不是setopt +o
使选项起作用。我正在使用 zsh 5.9 。
setopt nullglob
declare -a files=(${SOME_PATH}/*.log)
for f in "${files[@]}"; do
printf "Removing ${f}...\n"
rm -f "${f}" || printf "Failed to remove ${f}\n"
done
如果没有找到匹配,files
则数组为空,不会进入循环,完美满足我的需求。