当前位置: 首页 > 知识库问答 >
问题:

Bash中的动态变量名

越嘉石
2023-03-14

我对bash脚本感到困惑。

我有以下代码:

function grep_search() {
    magic_way_to_define_magic_variable_$1=`ls | tail -1`
    echo $magic_variable_$1
}

我希望能够创建一个变量名称,其中包含命令的第一个参数并带有例如ls的最后一行的值。

为了说明我想要什么:

$ ls | tail -1
stack-overflow.txt

$ grep_search() open_box
stack-overflow.txt

那么,我应该如何定义/声明$magic_way_to_define_magic_variable_1美元,我应该如何在脚本中调用它?

我试过eval${…}\$${…},但我仍然感到困惑。

共有3个答案

柳均
2023-03-14

除了关联数组之外,还有几种方法可以在 Bash 中实现动态变量。请注意,所有这些技术都存在风险,这些风险将在本答案的末尾进行讨论。

在下面的示例中,我将假设 i=37,并且您希望别名名为 var_37其初始值为 lolilol 的变量。

您可以简单地将变量的名称存储在一个间接变量中,这与C指针没有什么不同。Bash有一个读取别名变量的语法:< code>${!name}扩展为变量名< code>name的值。你可以把它想象成两阶段展开:< code>${!name}扩展为< code>$var_37,后者扩展为< code>lolilol。

name="var_$i"
echo "$name"         # outputs “var_37”
echo "${!name}"      # outputs “lolilol”
echo "${!name%lol}"  # outputs “loli”
# etc.

不幸的是,没有相应的语法来修改别名变量。相反,您可以通过以下技巧之一实现赋值。

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是邪恶的。

< code>read内建允许您将值赋给一个您给定名称的变量,这一事实可以与以下字符串结合使用:

IFS= read -r -d '' "$name" <<< 'babibab'
echo "$var_37"  # outputs “babibab\n”

IFS 部件和选项 -r 确保按原样分配值,而选项 -d '' 允许分配多行值。由于最后一个选项,该命令返回非零退出代码。

请注意,因为我们使用的是here-string,所以在值后面附加了一个换行符。

缺点:

    <李>有点晦涩; < li >返回非零退出代码; < li >向该值追加一个换行符。

从Bash 3.1(2005年发布)开始,printf内置还可以将其结果分配给给定名称的变量。与以前的解决方案相比,它只是有效的,不需要额外的努力来转义事物,防止分裂等等。

printf -v "$name" '%s' 'babibab'
echo "$var_37"  # outputs “babibab”

缺点:

  • 便携性较差(但,嗯)

从Bash 4.3(2014年发布)开始,< code>declare内置有一个选项< code>-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变量是通过它们的名称而不是它们的作用域来标识的,所以您可能会无意中为它自己创建一个别名(当您认为它会为封闭作用域中的一个变量起别名时)。这种情况在使用普通变量名(如< code>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!

资料来源:

    < li>BashFaq/006:如何使用可变变量(间接变量、指针、引用)或关联数组? < li>BashFAQ/048: eval命令和安全问题
齐向笛
2023-03-14

使用关联数组,命令名作为键。

# Requires bash 4, though
declare -A magic_variable=()

function grep_search() {
    magic_variable[$1]=$( ls | tail -1 )
    echo ${magic_variable[$1]}
}

如果您不能使用关联数组(例如,您必须支持bash3),您可以使用声明来创建动态变量名称:

declare "magic_variable_$1=$(ls | tail -1)"

并使用间接参数扩展来访问该值。

var="magic_variable_$1"
echo "${!var}"

请参阅 Bash 常见问题解答:间接 - 评估间接/参考变量。

包修贤
2023-03-14

我最近一直在寻找更好的方法。关联数组对我来说听起来有点过分了。看看我发现了什么:

suffix=bzz
declare prefix_$suffix=mystr

…然后…

varname=prefix_$suffix
echo ${!varname}

从文档中:

“$”字符引入了参数扩展、命令替换或算术扩展。...

参数扩展的基本形式是${参数}。参数的值被替换。…

如果参数的第一个字符是感叹号(!),而参数不是nameref,它会引入间接级别。Bash使用扩展参数其余部分形成的值作为新参数;然后进行扩展,该值用于扩展的其余部分,而不是原始参数的扩展。这称为间接扩展。该值受波浪号扩展、参数扩展、命令替换和算术扩展的影响。…

 类似资料:
  • 我写了一个bash脚本,有3个命令行选项,bib,bob和boo...我想将用户选项读入一个同名的bash变量,如下所示: 这一切都很好,到目前为止,很好。。。 但是现在我想把它扩展到一个包含许多选项的列表,所以与其写出一个很长的case语句,不如能够以某种方式循环一个选项列表并自动将选项传递给变量,沿着这些思路,这将是非常好的。 我使用声明语句将参数放入一个同名的动态变量中(请参阅Bash第二个

  • 问题内容: 我想使用运行时之前不知道的字段名称过滤器来调用查询…不确定如何构造变量名称…或者我很累。 如果funct()返回的名称等于 不知道该怎么做… 问题答案: 您可以创建字典,设置参数,然后通过解压缩字典作为关键字参数将其传递给函数:

  • 问题内容: 我想这样创建一个别名: 因此,如果有人输入: 它只会显示目录中的最后10个文件。 但似乎对我不起作用。有什么办法可以在bash中引入变量。 问题答案: 我会为此创建一个函数,而不是别名,然后将其导出,如下所示: 注意切换到:它告诉您您正在导出函数。把它放进去,你就很好了。

  • 问题内容: 因此,我正在尝试做某事,不确定是否可能。我有以下代码: 我想做的是为{0..5}的每个实例分配一个唯一的变量,因此为每个变量名指定group1 group2 group3 group4。然后,我将./user0更改为./user$i并根据我的序列创建动态变量列表。这可能吗?尝试执行此操作时出现以下错误,但我不确定bash不喜欢自己实际执行了什么操作。 test.sh:第16行:grou

  • 问题内容: 我现在想在“摘要”中添加一个变量$ {date}: 但是变量不会在bash中的单引号内扩展。 有可能这样做吗? 注意:GoogleCL是用python编写的命令行程序。我在使用Python 2.6的Ubuntu 10.10上。 问题答案: 我将在列表中添加另一个选项:将变量定义为换行符,然后在双引号中使用该变量。

  • 问题内容: 有什么方法可以根据html元素上的类设置颜色变量?还是通过其他方式实现这一目标? 问题答案: 这是基本主题。您可能想使用mixin或包含一个CSS文件中的多个主题。这是使用包括以下内容的方法: _theme.scss main.scss 您可以轻松地制作一个混合色,它使用3种颜色作为其参数来代替include: