我对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
、${…}
、\$${…}
,但我仍然感到困惑。
除了关联数组之外,还有几种方法可以在 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,所以在值后面附加了一个换行符。
缺点:
从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!
资料来源:
使用关联数组,命令名作为键。
# Requires bash 4, though
declare -A magic_variable=()
function grep_search() {
magic_variable[$1]=$( ls | tail -1 )
echo ${magic_variable[$1]}
}
如果您不能使用关联数组(例如,您必须支持bash
3),您可以使用声明
来创建动态变量名称:
declare "magic_variable_$1=$(ls | tail -1)"
并使用间接参数扩展来访问该值。
var="magic_variable_$1"
echo "${!var}"
请参阅 Bash 常见问题解答:间接 - 评估间接/参考变量。
我最近一直在寻找更好的方法。关联数组对我来说听起来有点过分了。看看我发现了什么:
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: