团队,
我正在尝试实现一种功能,该功能由用户检查 ENV 中预定义变量列表中所有变量的内容,然后使用所有结果变量作为参数运行我的函数。
所以,有3种可能性:
1 - All variables hold same value
[DatasetSize & BlockSize == DEFAULT ]
2 - All variables hold unique value
[DatasetSize & BlockSize != DEFAULT ]
3 - Some are unique and some distinct.
[DatasetSize || BlockSize = DEFAULT ]
我的代码在下面适用于案例 1 和案例 2,但不适用于案例 3。对于第三种情况,当任何变量具有“DEFAULT”时,它只执行第二个 elif 而不是第三个。
#!/bin/bash
#exit on fail without running next command
set -eo pipefail
export TestType="read"
export IOEngine="psync"
export DatasetSize="DEFAULT"
export BlockSize="NON-DEFAULT"
preset="DEFAULT"
declare -a static_vars=(TestType IOEngine)
declare -a dynamic_vars=(DatasetSize BlockSize )
declare -a vars=(TestType IOEngine DatasetSize BlockSize)
for var_name in "${vars[@]}"
do
if [ -z "$(eval "echo \$$var_name")" ]; then
echo "Missing environment variable $var_name"
exit 1
fi
done
overwrite_all() {
printf "all defaults in func\n"
}
overwrite_some() {
printf "some defaults in func\n"
}
overwrite_none() {
printf " none defaults in func\n"
}
overwrite() {
#not sure how to overwrite only those vars that have nonDEFAULT value.
export DatasetSize="changed"
export BlockSize="changed"
echo "DatasetSize=$DatasetSize BlockSize=$BlockSize"
}
if [[ "$IOEngine" == "psync" && ( "$TestType" == "read" || "$TestType" == "randread" ) ]]; then
iter=1
while [ $iter -lt 2 ]
do
echo "all are defaults; being to call your script with these parameters"
echo $var_name
if [[ ${dynamic_vars[@]} == $preset ]]; then
echo "calling over_none"
overwrite_none
elif [[ ${dynamic_vars[@]} != "DEFAULT" ]]; then
echo "calling over_all"
overwrite_all
overwrite
elif [[ $DatasetSize == "DEFAULT" || $BlockSize == "DEFAULT" ]] && [[ $DatasetSize != "DEFAULT" && $BlockSize != "DEFAULT" ]]; then
echo "calling over_some"
overwrite_some
overwrite
else
echo "done"
fi
iter=$[$iter+1]
done
else
echo "ITengine not found"
fi
echo "out of loop"
输出:
calling over_all
all non-defaults in func
Data setSize=changed BlockSize=changed
out of loop
期望输出:
some defaults in func
DatasetSize=changed BlockSize=changed
我将解决我认为是您的代码中的主要缺陷。它像这样一行:
你的片段
[[ ${dynamic_vars[@]} == $preset ]]
相当于这根本不符合您想要的逻辑。问题:
(小问题或设计使然)。
==
运算符内部未加引号的右侧[[ … ]]
被视为模式。DEFAULT
不包含*
也不包含,所以在这种情况下无关紧要;但通常你可能想要双引号$preset
。Unquoted
${array[@]}
像 unquoted$string
一样经历分词。如果你设置这个:then
${array[@]}
将扩展为三个实体,$string
. 但是"${array[@]}"
会扩展到两个实体,而"$string"
会扩展到一个实体。您可以使用以下代码自行测试:这意味着您实际上总是想使用
"${array[@]}"
,因为数组的全部意义在于传递精确数量的参数(此处:2),其中一些可能包含空格等。您的特定数组包含变量名称。一般来说,这样的名称不能包含空格或其他字符,这使它们安全(我认为即使涉及文件名扩展)。您可以将它们放在一个(空格分隔)
string
或一个中并通过,或(但不是)array
检索。这仍然是一个例外,它只起作用,因为变量的名称受到约束。${array[@]}
"${array[@]}"
$string
"$string"
所以你
${dynamic_vars[@]}
通常会扩展到两个实体("${dynamic_vars[@]}"
通常会)。不过有一个问题:无论如何,内部未加引号的变量[[ … ]]
都被视为单个实体(这对 来说是特殊的[[ … ]]
),因此"DatasetSize BlockSize"
.请注意,如果您这样做了
[[ "${dynamic_vars[@]}" == "$preset" ]]
,这将“有效”这在语法上是错误的( 的两边应该只有一个参数
==
)。但是
[[ "DatasetSize BlockSize" == DEFAULT ]]
nor[[ "DatasetSize" "BlockSize" == "DEFAULT" ]]
(甚至[[ "DatasetSize" == "DEFAULT" && "BlockSize" == "DEFAULT" ]]
,如果你以某种方式做到这一点)都不是你想要的。这将我们带到下一点。您的
dynamic_vars
数组包含字符串,而不是变量或它们的值。我的意思是您可以轻松地从中获取DatasetSize
(即文字DatasetSize
字符串)。这显然不同于$DatasetSize
扩展到的内容(即 stringDEFAULT
)。在将结果与 进行比较之前,您需要额外的扩展级别$preset
。您实际上在其他地方执行额外的扩展。在这个片段中:
eval "echo \$$var_name"
扩展名称存储在另一个变量中的变量是一种麻烦的方法(术语是间接扩展)。在 Bash 中有这样的特殊语法:(${!var_name}
通常你想用双引号引起来)。相关行可以是注意
"$(eval "echo \$$var_name")"
是危险的。如果最终变量在其他人的控制之下,他们可以将代码注入到您的脚本中。"${!var_name}"
在这件事上是安全的。同样在此代码段中,您使用了
for var_name in "${vars[@]}"
. 这是遍历数组的好方法。您可以使用这种方法来测试"${dynamic_vars[@]}"
代码后面的每个名称。把这些放在一起,你可以通过计算保持默认值的变量的数量来得到你想要的;像这样:
可以重建(优化)通用算法,因此尽快公布“混合”(最佳情况:测试前两个变量后)。然而,对于少数变量来说,这是不值得的;对于两个(或更少)变量,你总是需要检查它们。
请注意,我没有
if
按照您的原始订单测试 ( )。我在(可能性 1)0
之前测试(你的可能性 2$number_of_dynamic_vars
)。这样,如果dynamic_vars
数组恰好有 0 个元素,我将得到“无默认值”而不是“所有默认值”。这只是因为在这种极端情况下,我希望看到“无默认值”。但是如果“无默认值”意味着很多额外的工作而“所有默认值”没有,我想把这个边缘案例放在“所有默认值”篮子里(即$number_of_dynamic_vars
首先测试)。补充说明:
[
并且[[
是不同的。在大多数情况下,我使用[[
even if[
就足够了。[[ … ]]
,尽管在很多情况下不需要引用。我的前提是“总是引用你的变量,除非你知道你在做什么并且有充分的理由不这样做”。只有少数情况下您不能引用来获得您想要的东西;有些情况下引用并不重要;并且在很多情况下,缺乏适当的引用会让您感到痛苦。