运行“rm *”时如何摆脱“未找到匹配项”

运行“rm *”时如何摆脱“未找到匹配项”

使用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)?

  1. files=(*(N)); (($#files)) && rm -- $files。或者(rm -- *) 2> /dev/null,但这也会隐藏真正的错误,rm这是愚蠢的。您可以丢弃错误,但恢复命令zsh的 stderrrm(rm -- * 2>&3 3>&-) 3>&2 2> /dev/null
  2. emulate sh -c 'rm -- *' 2> /dev/null。然后,就像现在为该单个命令行模拟的sh那样zsh,不匹配的内容*按原样传递到rmrm抱怨该*文件不存在。我们抑制rm的 stderr ,就像您sh抑制该错误消息一样,但同样,这很愚蠢,因为它会隐藏真正的错误,rm而不是通过将sh文字传递*给 的不当行为而产生的错误rm。不过,rm -f '*'不会抱怨不存在的*文件,所以你可以这样做emulate sh -c 'rm -f -- *'
  3. 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(以及fishcshtcshbash -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 来自)确实表现得像cshor 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`

对我来说,nullglobcshnullglob工作

答案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则数组为空,不会进入循环,完美满足我的需求。

相关内容