#!/bin/bash
readonly x=2
function test {
local x=1
echo ${x}
}
test
echo $x
结果是,
readonly-local-test.sh: line 6: local: x: readonly variable
2
2
当变量是只读时,就会发生这种情况。但是,如果我删除只读限制,如下所示,
#!/bin/bash
x=2
function test {
local x=1
echo ${x}
}
test
echo $x
结果是,
1
2
为什么只读全局变量不能被隐藏?
答案1
bash好像不能shadow全球的只读变量。我怀疑这是 Posix 除了$@
.然而,当地的只读变量可以被隐藏。由于 Posix 中没有局部变量,因此它们的任何行为都是 posix 一致的。
readonly
始终定义全局变量。可以使用local -r
或在函数内部定义局部只读变量declare -r
(行为类似于local
函数内部)。
您可以修复您的脚本,将所有全局变量转换为局部变量*。例如,将所有内容包装在里面main() { YOUR_SCRIPT_HERE; }; main
并全部替换readonly
为local -r
:
#! /usr/bin/env bash
main() {
local -r x=constant
declare -p x
f() {
local x=mutable1
declare -p x
x=mutable2
declare -p x
}
f
declare -p x
# x=... would not work here
}
main
当然,而不是天真的修复“将所有的里面的脚本main
“您还可以在外部定义函数main
以获得更好的结构:
#! /usr/bin/env bash
main() {
local -r x=constant
declare -p x
f
declare -p x
# x=... would not work here
}
f() {
local x=mutable1
declare -p x
x=mutable2
declare -p x
}
main
* 请注意,在这两个版本中,当从 调用时, main
's x
(以前是全局变量)位于内部范围内。因此,内部的局部变量的行为大多与实际的全局变量相似。如果您习惯像.f
main
main
C
例子:
g() { local x=1; h; echo "g.x = $x"; }
h() { x=2; }
g
echo "global.x = $x";
… 印刷
g.x = 2
global.x =
答案2
据我所知,这种行为可以命名为“工作作为设计”。您有只读变量,并且您无法更改该变量的值。在 bash 中,您无法选择定义覆盖此类全局变量集的变量。
答案3
虽然在某些情况下这绝对是令人讨厌的行为,例如在OP中,但我明白这是一个很棒的功能。考虑一个全局只读变量应该就是这样。一旦设定,到处在脚本中该变量具有已知值。这包括任何深度的函数以及源文件带来的任何内容。
尽管这可能会产生所描述的冲突,但“通常”的缓解方法是使用命名约定来区分两种类型:ALL_CAPS_READONLY_GLOBAL
vs.snake_case_local
或camelCaseLocal
。
可写全局变量是真正的问题。我喜欢使用前导“_”(下划线)来表示它们是“特殊的”并且不应该被粗心地修改。事实上,我通常更喜欢为此类值提供 getter/setter 函数,如下所示:
declare -g _GLOBAL_FOO="default-value"
fooGet() { printf "%s" "${_GLOBAL_FOO}"; }
fooSet() { _GLOBAL_FOO="${1:-}"; }
对于数组,它更加复杂,但是这个想法可以扩展到索引和关联类型。另外,我实际上并没有做fooGet()
上面所示的事情;相反,我提供变量的名称来接收感兴趣的值。因此
local myVar=$(fooGet) ## okay, but...
local myVar; fooGet --var myVar ## nicer
fooGet --var myVar ## works, sort-of, but avoid this!!
实际上,这两种fooGet
方法在单个函数中运行良好。如果--var VARNAME
提供了参数,则通过 nameref 设置该值。否则,输出支持表单的值$(foo)
。
此外,如果在初始化/配置/启动期间需要全局可写,则添加另一个函数,例如:
fooFreeze() { declare -ga _GLOBAL_FOO; }
“锁定它”(即使其只读,同时保持当前值)。这在开始时允许灵活性,然后在初始化完成后使值不可变(且不可覆盖)。