我假设以下块代表一个函数,但它可能不是:
mounted()true
if
...
fi ... && mounted
我想了解这个结构是什么。我在函数标题下没有找到类似的语法 桀骜
实际代码的更完整片段是:
#! /bin/zsh -p
# leaving out a section...
tmpdir=$(mktemp -d) || exit
mounted()true
if
mount "$type[@]" -o "${(j[,])opts}" -- "$dev" "$tmpdir"
then
mount --bind -- "$tmpdir/$subdir" "$dest" || mounted()false
umount -- "$tmpdir"
fi && rmdir -- "$tmpdir" && mounted
一旦我理解了它,我就会将整个 zsh 脚本转换为我更熟悉的语言。作为中间步骤,我可能会将其转换为 bash。
答案1
这是定义函数的 Bourne shell 语法(来自 80 年代),它不是 zsh 特有的。
在 Bourne shell 中,函数是通过粘贴functionName()
在命令前面来定义的。
因此定义了一个 以简单命令作为其主体name() true
调用的函数。name
true
$ name()true
$ type name
name is a shell function
几乎所有类似 Bourne 的 shell(ksh、ash、dash、bosh、pdksh、mksh、zsh...)都是这种情况。一个值得注意的例外是bash
²(GNU shell),它最初只允许命令组( { ...; }
) 成为函数体,后来改为任何复合命令作为POSIX 对sh
语言的要求。
因此,在 中bash
,您需要name()((1))
or name()[[ . ]]
((( ... ))
and [[ ... ]]
— 从 ksh 借用的构造 — 被视为复合命令),或者name() { true; }
函数体是一个命令组,其中只有一个简单命令。
请注意,这ksh
是第一个引入函数的 shell,尽管具有不同的语法:function name { body; }
。zsh
支持 Korn 和 Bourne 语法,并有自己的扩展。
在zsh
see中info zsh function
,这应该会引导您进入有关 Korn 的部分function
关键词用于函数定义的语法。其扩展之一zsh
是,您可以使用同一主体同时定义多个函数,并使用任何字符串作为函数名称³:
vrai 1 + $'\1' () true
faux 0 - $'\0' '' () false
定义一些调用true
或false
不带参数的不同函数。
如果省略函数名称,它将成为匿名函数,它可以接受参数并当场调用:
() { echo There are $# non-hidden txt files; } *.txt(N)
(尽管出于明显的原因,为了使匿名函数能够接受参数,它的主体不能是一个简单的命令)。
在 中zsh
,函数也可以通过特殊的关联数组来使用$functions
,其中键是函数名称,值是主体中的代码。functions[name]=true
定义函数的另一种方法也是如此name
。
现在,使用函数来存储布尔值并不常见,但如果您停下来思考一下,它确实很有意义。
例如,在 C 语言中,if
/while
构造或&&
/||
逻辑运算符作用于数字。如果是非零数则if (condition) something
执行。或者哪些类似 C 的语言将其扩展为定义或非空字符串。something
condition
awk
perl
condition
但 shell 位于所有命令行解释器之前。在shell中,一切都是命令。if
/while
和&&
/||
在大多数 shell 中作用于命令。如果命令成功则if condition; then something; fi
执行。something
condition
false
和true
是布尔常量命令(内置于大多数 shell 中),它们总是分别失败/成功,因此它们是表示布尔值的明显命令。函数是存储命令(或一般的 shell 代码)最合适的数据结构。
alias
es(许多人认为 csh 是一个破坏了的继承,csh 是一个 shell(就像最初的 Bourne shell)没有函数)在这里不起作用,因为别名是在读取包含它们的代码时扩展的,而不是在运行时扩展的。例如在:
alias name=false
myfunction()
if name; then
something
fi
alias name=true
myfunction
函数的主体实际上将包含,if false; then...
因为name
别名是在读取定义函数的代码时扩展的,而不是在函数运行时扩展的。
可以将代码存储在变量而不是函数中:
name=true name=false
if eval " $name"; then
...
fi
我们测试命令是否成功,该命令被告知解释4eval
中的代码。请注意,在这种情况下,会产生一个空/未定义(除了选项)$name
nounset
$name
真的。
或者我们可以这样做(这就是我通常在sh
/bash
脚本中所做的):
name=true name=false
if "$name"; then
...
fi
我们运行名称存储在$name
不带参数的命令的地方。
或者:
name=(true) name=(false)
if "${name[@]}"; then
...
fi
我们存储 a 参数的地方简单的命令在一个$name
大批变量。
[
习惯使用类 C 语言的人可能希望将布尔值存储为整数变量,并运行/等命令,test
或者expr
测试从文本表示形式转换后的整数值:
false=0 true=1
name=$false name=$true
if expr "$name" > /dev/null; then
...
fi
if [ "$name" -ne 0 ]; then
...
fi
在类似 Korn 的 shell(包括bash
和zsh
)中,您可以使用((...))
计算类似 C 算术表达式的构造,并在生成非零数字(甚至 NaN)时返回成功。
false=0 true=1
name=$false name=$true
# or even:
name=false name=true
if (( name )); then
...
fi
您还可以运行一个命令来比较字符串(例如[
//test
再次expr
),或者像其他类似 Korn 的[[ string = pattern ]]
构造一样进行模式匹配:
name=true name=false
if [ "$name" = true ]; then
...
fi
(这对我来说就像if (strcmp(name, "true") == 0)...
在 C 中做 a 一样陌生)。
或者即使您愿意,也可以对已定义的变量进行类似awk
/的测试。perl
unset -v name # unset = false
name= # set = true
if [ -n "${name+true}" ]; then
...
fi
1 Bourne shell 中存在一个错误(不在其克隆/衍生版本5中),但如果您使用带有重定向的简单命令作为函数主体,则该错误无法正常工作,这可能就是 POSIX 仅需要复合命令的原因作为函数体得到支持。
² yash
(根据 POSIX 规范编写)posh
(基于pdksh
但编写是为了帮助验证是否符合标准,因此删除了标准的大多数扩展,包括该扩展)是另外两个例外
³ 这与外部命令和命令参数可以是任何字符串这一事实是一致的,因为文件可以包含任何字符串(尽管文件名不能为空并且文件名/参数不能包含 NUL 字节)。在 Bourne shell 中,函数和变量名称共享相同的命名空间(不能定义具有相同名称的函数和变量),并且函数名称与变量名称具有相同的限制。
4为确保正确性,在前面添加了一个空格,因为某些eval
实现不支持--
标记选项的结尾
5 zsh 本身过去在函数体中使用重定向时存在其自身的问题,其中大部分已得到修复。但即使是现在,正如文档中明确指出的, in f() { cmd; } < $1
,(在主体是带有重定向的命令组的特殊情况下)也不$1
是指$1
函数范围内的 ,而是指$1
调用者的该实例使其不符合 POSIX 标准。这不适用于简单命令或其他类型的复合命令(zsh
内部包含在 中{...}
)。