mja*_*mja 11 shell shell-script
我知道 Bash 和 Zsh 支持local变量,但有些系统只有 POSIX 兼容的 shell。并且local在 POSIX shell 中未定义。
所以我想问一下哪些shell支持local关键字定义局部变量?
编辑:关于外壳我的意思是默认/bin/sh外壳。
Sté*_*las 25
不是支持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
...
}
Run Code Online (Sandbox Code Playgroud)
(后来在 Bourne shell 中添加了函数支持,但使用了不同的语法 ( f() command) 并且后来也添加了对该语法的ksh支持;Bourne shell 从来没有本地作用域(当然通过子 shell 除外))
该local内置AFAIK首次加入Almquist外壳在1989年(在BSD系统,仪表板,busybox的SH使用),但显著不同于工作ksh的typeset。ash衍生品不支持typeset作为 的别名local,但您始终可以手动定义一个。
bash 和 zsh分别在 1989 年和 1991 年添加了typeset别名local。
ksh88local作为一个未公开的别名添加到typeset大约 1990 年和 pdksh 及其衍生产品于 1994 年。posh(基于pdksh)已删除typeset(严格遵守 Debian 政策,要求local,但不要求typeset)。
POSIX 最初反对指定typeset它是动态范围。所以 ksh93(1993 年 David Korn 对 ksh 的重写)转而使用静态作用域。同样在 ksh93 中,与 ksh88 不同,局部作用域仅对使用ksh语法 ( function f {...}) 而非 Bourne 语法 ( f() {...})声明的函数进行,并且local别名已被删除。
然而从AT&T的ksh93v-β和最终版本可与进行动态范围界定实验“的bash”模式(通过默认启用实际)(以麻烦的功能形式,包括与被编译local和typeset)时ksh93被调用作为bash。 local与typeset这种情况的不同之处在于它只能从函数内部调用。这bash模式将在默认情况下ksh2020被禁用,虽然该local/declare别名typeset,即使在未编译bash的模式将被保留(静态作用域虽仍)。
yash(写得很晚),有typeset(à la ksh88),但local自版本 2.48(2018 年 12 月)以来只有它的别名。
@Schily维护了一个 Bourne shell 后代,它最近主要符合 POSIX,称为bosh自 2016-07-06 版本以来支持本地范围(带有local,类似于ash)。
因此,如今具有某种形式的变量局部作用域的 Bourne-like shell 是:
就sh不同系统而言,请注意有些系统(大多数)POSIXsh在/bin其中,而其他系统则不在(例如 Solaris 中/usr/xpg4/bin)。对于sh在各种系统上的实施,我们有:
现在,它们的不同之处:
typeset(ksh、pdksh、bash、zsh、localyash )与(ksh88、pdksh、bash、zsh、ash、yash 2.48+)。function f {...}函数中),vs 动态范围(所有其他 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输出1或empty(另请参阅localvar_inheritbash 5.0 及更高版本中的选项)。export)和/或类型,以及哪些继承自父作用域中的变量。举例来说,无论是export V=1; f() { local V=2; printenv V; }; f打印1,2或没有。zsh)或最初未设置。unset V在局部范围的叶子变量的变量unset,或者只是剥离范围界定的一个水平(mksh,yash,bash在某些情况下)。例如,是否v=1; f() { local v=2; unset v; echo "$v"; }输出1或不输出(另请参阅localvar_unsetbash 5.0 及更高版本中的选项)export,无论它是关键字还是仅仅是内置的或两者兼而有之,以及在什么条件下它被视为关键字。export,参数是被解析为普通命令参数还是赋值(以及在什么条件下)。v=value myfunctionwheremyfunction本身声明v为本地与否的交互。我现在想到的就是这些。检查上面的奥斯汀组错误以获取更多详细信息。
至于外壳选项的局部范围(与变量相反),支持它的外壳是:
ksh88 (具有两种函数定义语法):默认情况下完成,我不知道有什么方法可以禁用它。ash(自 1989 年起):使用local -. 它使$-参数(存储选项列表)本地化。ksh93: 现在只为function f {...}函数做。zsh(自 1995 年以来)。与setopt localoptions. 还emulate -L用于将仿真模式(及其选项集)设为函数的本地。bash(自2016)与local -像ash,但仅限于所管理的选项set,而不是由管理的那些shopt。¹ shSolaris 上的 POSIX是/usr/xpg4/bin/sh(尽管它有许多一致性错误,包括函数本地的那些选项)。/bin/shSolaris 10 之前是 Bourne shell(所以没有本地作用域),因为 Solaris 11 是 ksh93
为了跟进 St\xc3\xa9phane\'s 答案中的提示,使用 subshells 可以获得局部效果。我无法访问真正的 POSIX shell,但这在 busybox 中有效ash——用()括号而不是{}大括号声明你的函数。这会强制该函数在子 shell 中运行。
func() (\n echo "in func, before declaring: x=$x"\n x=10\n echo "in func, after declaring: x=$x"\n)\n\nx=5\necho "before func: x=$x"\nfunc\necho "after func: x=$x"\nRun Code Online (Sandbox Code Playgroud)\n\n输出
\n\nbefore func: x=5\nin func, before declaring: x=5\nin func, after declaring: x=10\nafter func: x=5\nRun Code Online (Sandbox Code Playgroud)\n\n这表明该函数可以访问全局变量,并且在函数中设置变量不会改变全局变量。当我想要更改$IFS或cd更改目录时,我有时会使用这种技术,但我不希望这些操作影响程序的其余部分。