我定义了一个类似于以下的函数(作为别名),
testif() {
([ $1 ]) && echo "true" || echo "false"
}
..然后就可以打电话testit "1 == 2"
了巴什shell 和输出false
符合预期。但它不起作用桀骜。 zsh 中同一调用的输出是true
。
- 是否可以为 zsh 编写这样的函数?
- 为什么 zsh 输出错误? zsh bash 不应该兼容吗?
答案1
除了非常简单的例子之外,你所做的在 bash 中也不起作用。
$ testif 'hello world = hello world'
bash: [: too many arguments
false
$ testif '"hello world" = "hello world"'
bash: [: too many arguments
false
$ testif '* = *'
(usually false with an error message, but it depends on the files in the current directory)
即使你的简单示例在 zsh 中也不起作用,因为与其他类似 sh 的 shell 不同,在 zsh 中,它的$1
真正含义是“获取第一个参数的值”(几乎是:如果扩展导致空字,则将其完全删除)。在 bash 和其他类似 sh 的 shell 中,$1
意味着“获取第一个参数的值,将其拆分为单词,并将每个单词视为一个全局模式”,这几乎不是您想要的。
test
您可以在/single-brackets 命令的语法中传递条件,如下所示分离论据。
testif () {
if [ "$@" ]; then echo true; else echo false; fi
}
这是可行的(有命令的限制[
,例如 no&&
和||
操作符。
$ testif '*' = '*'
true
$ testif hello = hello
true
$ testif 'hello world' = 'hello world'
true
$ testif 0 = 00
false
$ testif 0 -eq 00
true
不过,很少有充分的理由来传递[
条件。您不妨传递整个命令。这不仅可以让您传递其他类型的条件(例如grep …
或if systemctl is-active …
),而且如果您需要的不仅仅是一个简单的命令,您可以定义一个辅助函数。
testif () {
if "$@"; then echo "true"; else echo "false"; fi
}
$ testif [ 'hello world' = 'hello world' ]
true
$ testif grep -q : /etc/passwd
true
另一种方法是将整个命令作为单个字符串传递,并eval
在函数内部使用。
testif () {
if eval "$1"; then echo true; else echo false; fi
}
$ testif '[ "hello world" = "hello world" ]'
true
$ testif '[ "hello world" = "h*" ]'
false
$ testif '[[ "hello world" = "h*" ]]'
false
$ testif '[[ "hello world" = h* ]]'
true