Wil*_*ard 9 bash shell-script wildcards
有没有一种很好的干净的传统方法来做到这一点?
例如,如果某个目录中有任何“.gz”文件,我想解压缩它们。但如果没有,我不想看到任何错误。
如果我使用gzip -d /mydir/*.gz
,我会收到错误消息:
gzip: /mydir/*.gz: No such file or directory
Run Code Online (Sandbox Code Playgroud)
如果我先shopt -s nullglob
然后gzip -d /mydir/*.gz
,我会得到以下信息:
gzip: compressed data not read from a terminal. Use -f to force decompression.
For help, type: gzip -h
Run Code Online (Sandbox Code Playgroud)
我有一种我知道可行的方法,我将其作为答案发布。我想知道是否有更好/更清洁的方法。
POSIX 兼容性是一个奖励,但不是必需的。
Sté*_*las 11
bash
:shopt -s nullglob
files=(/mydir/*.gz)
((${#files[@]} == 0)) || gzip -d -- "${files[@]}"
Run Code Online (Sandbox Code Playgroud)
zsh
:files=(/mydir/*.gz(N))
(($#files == 0)) || gzip -d -- $files
Run Code Online (Sandbox Code Playgroud)
请注意,在zsh
, without 中(N)
,就像在 Bourne 之前的 shell、csh 或 tcsh 中一样,如果 glob 不匹配,则不会运行该命令,您只能执行上述操作以避免产生错误消息(找不到匹配项为反对gzip
在bash
或其他类似 Bourne 的 shell的情况下在扩展的 glob上失败)。您可以使用bash
with获得相同的结果shopt -s failglob
。
在 中zsh
,失败的 glob 是一个致命错误,会导致 shell(非交互式时)退出。在这种情况下,您可以通过使用子shell 或使用zsh
错误捕获机制 ( { try-block; } always { error-catching; }
)、(或通过设置nonomatch
(像 一样工作sh
)nullglob
或noglob
选项来防止脚本在这种情况下退出,尽管我不建议这样做):
$ zsh -c 'echo zz*; echo not output'
zsh:1: no matches found: zz*
$ zsh -c '(echo zz*); echo output'
zsh:1: no matches found: zz*
output
$ zsh -c '{echo zz*;} always {TRY_BLOCK_ERROR=0;}; echo output'
zsh:1: no matches found: zz*
output
$ zsh -o nonomatch -c 'echo zz*; echo output'
zz*
output
Run Code Online (Sandbox Code Playgroud)
ksh93
最终加入类似的机制zsh
的(N)
水珠限定符来避免不必设置一个nullglob
全局选项:
files=(~(N)/mydir/*.gz)
((${#files[@]} == 0)) || gzip -d -- "${files[@]}"
Run Code Online (Sandbox Code Playgroud)
可移植地在 POSIX 中sh
,其中不匹配的 glob 未扩展地传递而无法禁用该行为(唯一与 POSIX glob 相关的选项是noglob
完全禁用 globbing),诀窍是执行以下操作:
set -- /mydir/[*].gz /mydir/*.gz
case $#$1$2 in
'2/mydir/[*].gz/mydir/*.gz') : no match;;
*) shift; gzip -d -- "$@"
esac
Run Code Online (Sandbox Code Playgroud)
这个想法是,如果/mydir/*.gz
不匹配,那么它将扩展到自身 ( /mydir/*.gz
)。然而,如果有一个文件被实际调用,它也可以扩展为/mydir/*.gz
,因此为了区分情况,我们还使用/mydir/[*].gz
glob,/mydir/*.gz
如果有一个文件被调用,它也会扩展到。
由于这很尴尬,您可能更喜欢find
在这些情况下使用:
find /mydir/. ! -name . -prune ! -name '.*' \
-name '*.gz' -type f -exec gzip -d {} +
Run Code Online (Sandbox Code Playgroud)
的! -name . -prune
是不考虑子目录(一些find
实施方式中具有-depth 1
或-mindepth 1 -maxdepth 1
作为等效)。! -name '.*'
是像 globs 那样排除隐藏文件。
一个好处是,如果文件列表太大而无法满足执行命令的参数大小限制,它仍然有效(如果需要避免这种情况,find
将运行多个gzip
命令,ksh93
并且zsh
还有解决该问题的机制)。
另一个好处是,如果find
无法读取文件的内容/mydir
或无法确定文件的类型,您将收到错误消息(globs 只会默默地忽略该问题并表现为相应的文件不存在)。
一个小的缺点是你失去了gzip
退出状态的确切值(如果任何一个gzip
调用以非零退出状态失败,find
但仍然会以非零退出状态退出(尽管不一定相同),所以这对于大多数用例来说已经足够了)。
另一个好处是您可以添加-type f
以避免尝试解压缩名称以.gz
. 除了 in zsh
(*.gz(.)
仅适用于常规文件),globs 无法按文件类型过滤,您需要执行以下操作:
set --
for f in /mydir/*.gz
[ -f "$f" ] && [ ! -L "$f" ] && set -- "$@" "$f"
done
[ "$#" -eq 0 ] || gzip -d -- "$@"
Run Code Online (Sandbox Code Playgroud)