我搜索了很久,也没找到任何关于此内容的信息。
在下面的代码中,Bar1
其行为就像我期望“常规”变量的行为一样(因为help declare
说它只-g
在函数调用中“看到”):它根据范围(外部或内部)改变值。
Bar2
,被“明确”声明,也像我期望的那样(gfhjnfd
在外部范围内和blat
内部范围内)。
但是Foo
是全局不变的变量。 help declare
只说“使 NAME 成为只读”。这个扩展(和不需要的)功能记录在哪里?
$ cat test.sh
#!/bin/bash
declare -r Foo=bar
declare -g Bar1=fdhtbn
declare Bar2=gfhjnfd
X()
{
local Foo=$1
local Bar1=snorkel
local Bar2=$2
echo Foo in X = $Foo
echo Bar1 in X = $Bar1
echo Bar2 in X = $Bar2
}
echo Foo, above X, = $Foo
echo Bar1, above X = $Bar1
echo Bar2, above X = $Bar2
echo
X baz blat
echo
echo Foo, below X, = $Foo
echo Bar1, below X = $Bar1
echo Bar2, below X = $Bar2
$ ./test.sh
Foo, above X, = bar
Bar1, above X = fdhtbn
Bar2, above X = gfhjnfd
./test.sh: line 9: local: Foo: readonly variable
Foo in X = bar
Bar1 in X = snorkel
Bar2 in X = blat
Foo, below X, = bar
Bar1, below X = fdhtbn
Bar2, below X = gfhjnfd
在一个范围内是否
readonly
会阻止在子范围内声明同名变量(在具有动态范围的 shell 中,例如 bash)在不同的 shell 之间有所不同。这是在奥斯汀小组讨论中提出的,讨论内容是指定
local
POSIX 中的变量范围sh
(由于无法就此和其他很多方面达成共识,因此这一努力现已被放弃)。在 bash 中,这是设计使然,维护者表示他们不会对此做出改变(至少在默认环境中不会),因为这被认为是一种安全措施。
readonly
可以用于两个不完全兼容的用途:const
C,尽管这里它发生在运行时)。$PATH
,)$HISTFILE
(尽管想要提供受限 shell 环境的管理员可能希望将更多变量设置为只读,例如LD_PRELOAD
,PERL5LIB
...)需要保持只读以防止用户绕过限制。在 bash 中,
$PATH
受限 shell 中对 (仅具有允许的命令) 的修改依赖于属性readonly
,因此即使在子范围内也不能修改这些变量(包括它们的属性或范围列表),这一点很重要。在 zsh 中,
$PATH
和一些其他变量(包括LD_PRELOAD
,但不包括PERL5LIB
,PYTHONPATH
...)受到限制,但不是通过只读属性限制的,因此readonly
可以可靠地用于上面提到的第一个用法,但这意味着如果你的受限环境中有一些用perl
或写成的命令python
,用户可能能够通过PERL5LIB
/PYTHONPATH
环境变量绕过限制,例如,将这些变量设置为只读不会有帮助,因为用户可以typeset +r PERL5LIB
删除该只读属性或在具有非只读属性的子范围中隐藏它。¹有关各种 POSIX 类 shell 中的本地范围的更多详细信息,请参阅支持使用 `local` 关键字定义局部变量的 shell 列表。
这里的根本问题是关键字实际上
local
并没有创建局部变量,或者至少不是您所期望的那样。根据Bash 参考手册:
和
(其中粗体表示我强调)。
从计算机科学术语上讲,Bash 使用动态作用域而不是词法作用域;但从生活在 2024 年的典型程序员的角度来看,这实际上意味着它
local
不会创建局部变量,而只是暂时修改全局变量。它的特殊魔力在于,当函数返回时,它会自动恢复变量的原始值(和其他属性),这很有用,但并不是您所期望的“局部变量”的完整实现。因此,如果
local
可以覆盖readonly
,则如下所示:将会打印
4
而不是3
——变量three
实际上不是只读的。正如 Stéphane Chazelas 在他的回答中指出的那样,一些 shell确实允许
local
覆盖readonly
。但我认为,在任何local
具有与 Bash 相同基本行为的 shell 中,这都是一个错误(即,临时修改全局变量而不是创建单独的局部变量),并且该行为确实与 的意图相冲突readonly
。我只能假设他们的动机是让local
变量更像真正的局部变量,但只要它们不是真正的局部变量,这就没有帮助。这记录
local
在www.gnu.org/software/bash/manual/bash.html#Bash-Builtins
我确实觉得这很令人惊讶,因为第一句话就是:
这意味着将创建一个新变量,其作用域仅限于该函数。有一种观点认为,全局常量应该得到可靠保存。