除了关联数组之外,还有多种方法可以在Bash中实现动态变量。请注意,所有这些技术都存在风险,将在本答案的最后进行讨论。
在下面的示例中,我将假定i=37
并且您要为var_37
初始值为的名为的变量添加别名lolilol
。
方法1.使用“指针”变量
您可以简单地将变量的名称存储在间接变量中,与C指针不同。然后,Bash具有读取别名变量的语法:${!name}
扩展为名称为变量值的变量的值name
。您可以将其视为两个阶段的扩展:${!name}
扩展为$var_37
,扩展为lolilol
。
name="var_$i"
echo "$name" # outputs “var_37”
echo "${!name}" # outputs “lolilol”
echo "${!name%lol}" # outputs “loli”
# etc.
不幸的是,没有用于修改别名变量的对应语法。相反,您可以使用以下技巧之一实现分配。
1a。分配给eval
eval
是邪恶的,但也是实现我们目标的最简单,最便携的方法。您必须小心地逃避作业的右侧,因为它将被评估两次。一种简单而系统的方法是事先评估右侧(或使用printf %q
)。
并且您应该手动检查左侧是否为有效的变量名或带索引的名称(如果是的话evil_code #
)。相比之下,下面的所有其他方法都会自动执行它。
# check that name is a valid variable name:
# note: this code does not support variable_name[index]
shopt -s globasciiranges
[[ "$name" == [a-zA-Z_]*([a-zA-Z_0-9]) ]] || exit
value='babibab'
eval "$name"='$value' # carefully escape the right-hand side!
echo "$var_37" # outputs “babibab”
缺点:
- 不检查变量名的有效性。
eval
是邪恶的。
eval
是邪恶的。
eval
是邪恶的。
1b。分配给read
该read
内建让您指定值,其中你给的名称,可以在这里与弦一起利用的事实变量:
IFS= read -r -d '' "$name" <<< 'babibab'
echo "$var_37" # outputs “babibab\n”
该IFS
部分和选项-r
确保该值被分配原样,而选项-d ''
允许指定多行值。由于使用了最后一个选项,因此该命令返回的退出代码为非零。
请注意,由于我们使用的是here字符串,因此在值后附加了换行符。
缺点:
- 有点晦涩
- 返回非零退出代码;
- 在值后附加一个换行符。
1c。分配给printf
从Bash 3.1(2005年发布)开始,printf
内建函数也可以将其结果分配给已命名的变量。与以前的解决方案相比,它只是有效,不需要额外的工作来逃避事物,防止分裂等。
printf -v "$name" '%s' 'babibab'
echo "$var_37" # outputs “babibab”
缺点:
方法2。使用“引用”变量
自Bash 4.3(2014年发布)以来,declare
内置的选项-n
可以创建一个变量,该变量是对另一个变量的“名称引用”,就像C ++引用一样。就像方法1中一样,引用存储别名变量的名称,但是每次访问引用(用于读取或分配)时,Bash都会自动解析该间接引用。
此外,Bash具有一种特殊且非常混乱的语法,用于获取引用本身的值,请自己判断:${!ref}
。
declare -n ref="var_$i"
echo "${!ref}" # outputs “var_37”
echo "$ref" # outputs “lolilol”
ref='babibab'
echo "$var_37" # outputs “babibab”
这不能避免下面解释的陷阱,但至少可以使语法简单明了。
缺点:
风险性
所有这些别名技术都存在一些风险。第一个是每次您解决间接寻址(用于读取或分配)时,执行任意代码。确实,var_37
除了标量变量名(如)外,还可以给数组下标(如)加上别名arr[42]
。但是Bash每次都需要评估方括号的内容,因此混叠arr[$(do_evil)]
会产生意想不到的效果……因此,只有在控制别名的来源时才使用这些技术。
function guillemots() {
declare -n var="$1"
var="«${var}»"
}
arr=( aaa bbb ccc )
guillemots 'arr[1]' # modifies the second cell of the array, as expected
guillemots 'arr[$(date>>date.out)1]' # writes twice into date.out
# (once when expanding var, once when assigning to it)
第二个风险是创建循环别名。由于Bash变量是通过它们的名称而不是它们的作用域来标识的,因此您可能会无意中为其自身创建一个别名(同时认为它会在一个封闭的作用域中为一个变量命名)。在使用通用变量名称(例如var
)时,尤其可能发生这种情况。因此,仅在控制别名变量的名称时才使用这些技术。
function guillemots() {
# var is intended to be local to the function,
# aliasing a variable which comes from outside
declare -n var="$1"
var="«${var}»"
}
var='lolilol'
guillemots var # Bash warnings: “var: circular name reference”
echo "$var" # outputs anything!
资源: