jhr*_*jhr 3 shell scripting zsh wildcards
我目前正在编写一个非常简单的 zsh 脚本。我经常做的是这样的:
mv */*.{a,b} .
Run Code Online (Sandbox Code Playgroud)
当我在 zsh 脚本中运行它时,它似乎以不同的方式扩展并在交互模式下工作时失败。
% mkdir dir
% touch dir/file.a
% ls file.a
ls: cannot access file.a: No such file or directory
% mv */*.{a,b} .
% ls file.a
file.a
Run Code Online (Sandbox Code Playgroud)
所以,这有效,但作为脚本:
% mkdir dir
% touch dir/file.a
% ls file.a
ls: cannot access file.a: No such file or directory
% cat script.sh
#!/usr/bin/zsh
mv */*.{a,b} .
% ./script.sh
./script.sh:2: no matches found: */*.b
Run Code Online (Sandbox Code Playgroud)
那么,有什么不同呢?我究竟做错了什么?
两者的zsh
默认选项设置都是错误的。您可以使用echo
as 命令而不是mv
.
从交互上看,您似乎已经null_glob
设置了选项。根据zsh
文档,默认情况下未设置该选项。未设置该选项会发生什么取决于是否nomatch
已设置或未设置另一个选项。使用nomatch
unset ( nonomatch
) 你会得到这个:
% mkdir dir
% touch dir/file.a
% ls file.a
ls: cannot access file.a: No such file or directory
% echo */*.{a,b} .
dir/file.a */*.b .
Run Code Online (Sandbox Code Playgroud)
扩展分两步进行。首先,*/*.{a,b}
扩展为2个词:*/*.a
和*/*.b
。然后每个单词被扩展为一个 glob 模式。第一个扩展为dir/file.a
,第二个扩展为自身,因为它不匹配任何内容。所有这一切意味着,如果您使用mv
和 不echo
,mv
应该尝试移动 2 个文件:(dir/file.a
很好)和*/*.b
(没有这样的文件)。这是大多数 shell 中默认发生的情况,例如sh
andksg
和bash
。
zsh 默认选项设置是null_glob
未设置和nomatch
已设置。脚本使用默认选项设置运行(除非您在~/.zshenv
或 中更改它们/etc/zshenv
,您不应该这样做)。这意味着在脚本中,您会得到:
% mkdir dir
% touch dir/file.a
% ls file.a
ls: cannot access file.a: No such file or directory
% cat script.sh
#!/usr/bin/zsh
echo */*.{a,b} .
% ./script.sh
./script.sh:2: no matches found: */*.b
Run Code Online (Sandbox Code Playgroud)
由于*/*.b
不匹配任何内容,因此您会因nomatch
.
如果你setopt nonomatch
在echo
/mv
命令之前插入脚本,你会回到我上面描述的错误行为:它试图移动一个不存在的文件。
如果你setopt null_glob
在echo
/mv
命令之前插入脚本,你会得到你在交互式 shell 中得到的行为,这是有效的。