在 Zsh 中组合文件测试
Combining file tests in Zsh
zsh 中最优雅的测试方式是什么,文件是否是可读的常规文件?
我知道我可以做类似的事情
if [[ -r "$name" && -f "$name" ]]
...
但它需要重复两次“$name”。我知道我们不能组合条件 (-rf $name),但也许可以使用 zsh 中的其他一些功能?
顺便说一句,我也考虑过
if ls ${name}(R.) >/dev/null 2>&1
...
但在这种情况下,当 $name 不满足条件时,shell 会抱怨 "no matches found"。设置 NULL_GLOB 在这里也无济于事,因为它只会用空字符串替换模式,并且表达式始终为真。
在非常新的 zsh 版本中(适用于 5.0.7,但不适用于 5.0.5),您可以这样做
setopt EXTENDED_GLOB
if [[ -n $name(#qNR.) ]]
...
$name(#qNR.)
匹配名称为 $name
的可读 (R
) 和常规 (.
) 文件。 N
为该匹配项启用 NULL_GLOB
。也就是说,如果没有文件与模式匹配,它不会产生错误,但会从参数列表中删除。 -n
检查匹配是否实际上是非空的。需要 EXTENDED_GLOB
来启用 (#q...)
类型的扩展通配符,这又是必需的,因为括号在条件表达式 ([[ ... ]]
) 中通常具有不同的含义。
不过,虽然确实可以写出只使用一次 $name
的东西,但我还是建议不要这样做。它比原来的解决方案更复杂,因此对于下一个阅读它的人来说更难理解(即需要思考)(最多半年后你未来的自己算作 "next guy")。至少这个解决方案只适用于 zsh 并且只适用于新版本,而原始版本 运行 在 bash.
像你提到的那样使小(?)shell 函数怎么样?
tests-raw () {
setopt localoptions no_ksharrays
local then=""; shift
local f="${@[-1]}" t=
local -i ret=0
set -- "${@[1,-2]}"
for t in ${@[@]}; do
if test "$t" "$f"; then
ret=$?
"$then"
else
return $?
fi
done
return ret
}
and () tests-raw continue "${@[@]}";
or () tests-raw break "${@[@]}";
# examples
name=/dev/null
if and -r -c "$name"; then
echo 'Ok, it is a readable+character special file.'
fi
#>> Ok, it is...
and -r -f ~/.zshrc ; echo $? #>> 0
or -r -d ~/.zshrc ; echo $? #>> 0
and -r -d ~/.zshrc ; echo $? #>> 1
# It could be `and -rd ~/.zshrc` possible.
虽然我觉得这有点矫枉过正。
zsh 中最优雅的测试方式是什么,文件是否是可读的常规文件?
我知道我可以做类似的事情
if [[ -r "$name" && -f "$name" ]]
...
但它需要重复两次“$name”。我知道我们不能组合条件 (-rf $name),但也许可以使用 zsh 中的其他一些功能?
顺便说一句,我也考虑过
if ls ${name}(R.) >/dev/null 2>&1
...
但在这种情况下,当 $name 不满足条件时,shell 会抱怨 "no matches found"。设置 NULL_GLOB 在这里也无济于事,因为它只会用空字符串替换模式,并且表达式始终为真。
在非常新的 zsh 版本中(适用于 5.0.7,但不适用于 5.0.5),您可以这样做
setopt EXTENDED_GLOB
if [[ -n $name(#qNR.) ]]
...
$name(#qNR.)
匹配名称为 $name
的可读 (R
) 和常规 (.
) 文件。 N
为该匹配项启用 NULL_GLOB
。也就是说,如果没有文件与模式匹配,它不会产生错误,但会从参数列表中删除。 -n
检查匹配是否实际上是非空的。需要 EXTENDED_GLOB
来启用 (#q...)
类型的扩展通配符,这又是必需的,因为括号在条件表达式 ([[ ... ]]
) 中通常具有不同的含义。
不过,虽然确实可以写出只使用一次 $name
的东西,但我还是建议不要这样做。它比原来的解决方案更复杂,因此对于下一个阅读它的人来说更难理解(即需要思考)(最多半年后你未来的自己算作 "next guy")。至少这个解决方案只适用于 zsh 并且只适用于新版本,而原始版本 运行 在 bash.
像你提到的那样使小(?)shell 函数怎么样?
tests-raw () {
setopt localoptions no_ksharrays
local then=""; shift
local f="${@[-1]}" t=
local -i ret=0
set -- "${@[1,-2]}"
for t in ${@[@]}; do
if test "$t" "$f"; then
ret=$?
"$then"
else
return $?
fi
done
return ret
}
and () tests-raw continue "${@[@]}";
or () tests-raw break "${@[@]}";
# examples
name=/dev/null
if and -r -c "$name"; then
echo 'Ok, it is a readable+character special file.'
fi
#>> Ok, it is...
and -r -f ~/.zshrc ; echo $? #>> 0
or -r -d ~/.zshrc ; echo $? #>> 0
and -r -d ~/.zshrc ; echo $? #>> 1
# It could be `and -rd ~/.zshrc` possible.
虽然我觉得这有点矫枉过正。