支持“local”关键字定义局部变量的 shell 列表

支持“local”关键字定义局部变量的 shell 列表

我知道 Bash 和 Zsh 支持local变量,但有些系统只有 POSIX 兼容的 shell。并且local在 POSIX shell 中未定义。

所以我想问一下哪些shell支持local关键字定义局部变量?

编辑:关于 shell 我指的是默认/bin/shshell。

答案1

这不是支持与local不支持那么简单。具有一种或另一种本地作用域形式的 shell 之间的语法及其执行方式有很多变化。

这就是为什么很难提出一个所有人都同意的标准。看http://austingroupbugs.net/bug_view_page.php?bug_id=767感谢 POSIX 在这方面所做的努力。

本地作用域首先在 80 年代初添加到 ksh 中。

在函数中声明局部变量的语法是typeset

function f {
  typeset var=value
  set -o noglob # also local to the function
  ...
}

(后来在 Bourne shell 中添加了函数支持,但使用了不同的语法 ( f() command),并且ksh后来也添加了对该语法的支持;Bourne shell 从未具有本地作用域(当然通过子 shell 除外))

内置的AFAIKlocal于 1989 年首先添加到 Almquist shell(用于 BSD、dash、busybox sh),但工作方式与ksh的显着不同typesetash衍生品不支持typeset作为 的别名local,但您始终可以手动定义一个。

bash 和 zsh分别于 1989 年和 1991 年添加了typeset别名。local

ksh88 在 1990 年左右添加local为未记录的别名typeset,在 1994 年添加为 pdksh 及其衍生物。posh(基于pdksh)已删除typeset(为了严格遵守需要 的 Debian 政策local,但不需要typeset)。

POSIX 最初反对指定typeset理由是它是动态的范围界定。因此 ksh93(David Korn 在 1993 年重写了 ksh)改为静止的相反,范围界定。同样,在 ksh93 中,与 ksh88 不同,局部作用域仅针对使用ksh语法 ( function f {...}) 声明的函数完成,而不是 Bourne 语法 ( f() {...}),并且local别名已被删除。

然而,来自 AT&T 的 ksh93v- beta 和最终版本可以使用实验性“bash”模式(实际上默认启用)进行编译,该模式在作为 调用时执行动态作用域(以函数形式,包括 withlocaltypeset)。 与这种情况的不同之处在于它只能从函数内部调用。ksh93bashlocaltypesetbashksh2020 中默认禁用尽管local/别名declaretypeset即使未编译 bash 模式,(尽管仍然具有静态范围)。

yash(写得很晚),有typeset(à la ksh88),但local自版本 2.48(2018 年 12 月)以来仅作为它的别名。

@希利(谁可悲的是于2021年去世)用于维护 Bourne shell 后代,最近已基本符合 POSIX 标准,称为bosh自 2016-07-06 版本起支持本地范围(与local类似ash)。

因此,如今具有某种形式的局部变量作用域的类似 Bourne 的 shell 是:

  • ksh、所有实现及其衍生产品(ksh88、ksh93、pdksh 以及 posh、mksh、OpenBSD sh 等衍生产品)。
  • ash 及其所有衍生产品(NetBSD sh、FreeBSD sh、dash、busybox sh)
  • 巴什
  • 桀骜
  • 亚什
  • 波什

就不同系统而言,请注意,有些系统(大多数)包含shPOSIX ,而另一些系统则不包含(例如 Solaris )。为了在各种系统上实施,我们有:sh/bin/usr/xpg4/binsh

  • ksh88:大多数 SysV 衍生的商业 Unices(AIX、HP/UX、Solaris1...)
  • bash:大多数 GNU/Linux 系统、Cygwin、macOS
  • ash:默认情况下,在 Debian 和大多数衍生产品(包括 Ubuntu、Linux/Mint)上,管理员可以将其更改为 bash 或 mksh。 NetBSD、FreeBSD 及其一些衍生产品(不包括 macOS)。
  • busybox sh:许多(如果不是大多数)嵌入式 Linux 系统
  • pdksh 或衍生产品:OpenBSD、MirBSD、Android

现在,他们的不同之处:

  • typeset(ksh、pdksh、bash、zsh、yash)与local(ksh88、pdksh、bash、zsh、ash、yash 2.48+)。
  • 支持的选项列表。
  • 静态(ksh93,在function f {...}函数中)与动态作用域(所有其他 shell)。例如,无论是function f { typeset v=1; g; echo "$v"; }; function g { v=2; }; f输出1还是2.另请参阅该export属性如何影响ksh93.
  • 无论local/typeset只是创建变量当地的( ash, bosh),或创建变量的新实例(其他 shell)。例如,是否v=1; f() { local v; echo "${v:-empty}"; }; f输出1empty(另请参阅localvar_inheritbash 5.0 及更高版本中的选项)。
  • 对于创建新变量的变量,新变量是否继承属性(如export)和/或类型以及哪些来自父作用域中的变量。例如,是否export V=1; f() { local V=2; printenv V; }; f打印12或什么也不打印。
  • 该新变量是否具有初始值(空、0、空列表,取决于类型zsh)或最初未设置。
  • 是否unset V在局部作用域中的变量上留下该变量unset,或者只是果皮一级范围(在某些情况下mkshyash, bash)。例如,是否v=1; f() { local v=2; unset v; echo "$v"; }输出1或不输出(另请参阅localvar_unsetbash 5.0 及更高版本中的选项)
  • 就像 for 一样export,它是一个关键字还是只是一个内置关键字,或者两者兼而有之,以及在什么条件下它被视为关键字。
  • 与 for 一样export,参数是否被解析为普通命令参数或赋值(以及在什么条件下)。
  • 您是否可以声明当地的在父作用域中只读的变量。
  • v=value myfunction与where本身的交互myfunction声明v为本地或非本地。

我现在想到的就是这些。检查上面的奥斯汀组错误以获取更多详细信息。

至于 shell 的本地作用域选项(相对于变量),支持它的 shell 是:

  • ksh88(使用两个函数定义语法):默认完成,我不知道有什么方法可以禁用它。
  • ash(自 1989 年起):与local -.它将$-参数(存储选项列表)设置为本地参数。
  • ksh93function f {...}:现在仅针对功能完成。
  • zsh(自 1995 年起)。和setopt localoptions。还可以emulate -L将仿真模式(及其选项集)设置为函数本地化。
  • bash(自 2016 年起)与local -类似ash,但仅适用于 管理的选项set,而不是 管理的选项shopt

sh1 Solaris 上的POSIX是/usr/xpg4/bin/sh(尽管它有许多一致性错误,包括那些函数本地选项)。/bin/shSolaris 10 之前是 Bourne shell(因此没有本地作用域),自 Solaris 11 开始是 ksh93

答案2

为了跟进 Stéphane 答案中的提示,使用子 shell 可以获得本地影响。我无法访问真正的 POSIX shell,但这在 busybox 中有效——用括号而不是大括号ash声明你的函数。这会强制该函数在子 shell 中运行。(){}

func() (
    echo "in func, before declaring: x=$x"
    x=10
    echo "in func, after declaring: x=$x"
)

x=5
echo "before func: x=$x"
func
echo "after func: x=$x"

输出

before func: x=5
in func, before declaring: x=5
in func, after declaring: x=10
after func: x=5

这表明该函数可以访问全局变量,并且在函数中设置变量不会改变全局变量。当我想要更改$IFS或更改目录时,我有时会使用这种技术cd,但我不希望这些操作影响程序的其余部分。

相关内容