zsh 语法是什么?名称()真实

zsh 语法是什么?名称()真实

我假设以下块代表一个函数,但它可能不是:

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调用的函数。nametrue

$ 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 语法,并有自己的扩展。

zshsee中info zsh function,这应该会引导您进入有关 Korn 的部分function关键词用于函数定义的语法。其扩展之一zsh是,您可以使用同一主体同时定义多个函数,并使用任何字符串作为函数名称³:

vrai 1 + $'\1' () true
faux 0 - $'\0' '' () false

定义一些调用truefalse不带参数的不同函数。

如果省略函数名称,它将成为匿名函数,它可以接受参数并当场调用:

() { echo There are $# non-hidden txt files; } *.txt(N)

(尽管出于明显的原因,为了使匿名函数能够接受参数,它的主体不能是一个简单的命令)。

在 中zsh,函数也可以通过特殊的关联数组来使用$functions,其中键是函数名称,值是主体中的代码。functions[name]=true定义函数的另一种方法也是如此name


现在,使用函数来存储布尔值并不常见,但如果您停下来思考一下,它确实很有意义。

例如,在 C 语言中,if/while构造或&&/||逻辑运算符作用于数字。如果是非零数则if (condition) something执行。或者哪些类似 C 的语言将其扩展为定义或非空字符串。somethingconditionawkperlcondition

但 shell 位于所有命令行解释器之前。在shell中,一切都是命令。if/while&&/||在大多数 shell 中作用于命令。如果命令成功则if condition; then something; fi执行。somethingcondition

falsetrue是布尔常量命令(内置于大多数 shell 中),它们总是分别失败/成功,因此它们是表示布尔值的明显命令。函数是存储命令(或一般的 shell 代码)最合适的数据结构。

aliases(许多人认为 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中的代码。请注意,在这种情况下,会产生一个空/未定义(除了选项)$namenounset$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(包括bashzsh)中,您可以使用((...))计算类似 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内部包含在 中{...})。

相关内容