团队,我有一组参数,它们在这个脚本 A 中定义,默认值为“DEFAULT”。当用户将这些变量参数设置为除“DEFAULT”以外的任何值时,我必须运行另一个具有更新变量值的脚本,否则我将运行另一个具有默认值的脚本 B。所以,我有三种情况
我正在脚本中模拟 jenkins 的参数设置。DEFAULT 值在 jenkins 作业上预设为环境变量,然后运行此脚本。因此,如果用户不更改任何参数并让所有 fio_gen 的值都为 DEFAULT,那么它们将被我在脚本中定义的值替换。
1-Run Script-B with DEFAULT variable values
2-Run Script-B with all-non-DEFAULT variable values
3-Run Script-B with mixed variable values.. some default and some non.
下面是我的代码,它标识了哪些变量是默认的,哪些不是默认的,但是我想不出对上述混合执行 case-3 的逻辑。
我是否应该在 vars_ioengine_defaults 中使用关联数组并进行比较和使用?或者什么是好的方法?
#!/bin/bash
declare -a vars=(IOEngine TestType Threads BlockSize FileSize DatasetSize QueueDepth RunTime UDCName)
for var_name in "${vars[@]}"
do
if [ -z "${!var_name}" ]; then
echo "Missing environment variable $var_name"
exit 1
fi
done
#***Comment unComment to test 3 cases"
#DatasetSize="DEFAULT"
#BlockSize="DEFAULT"
#DatasetSize="nonDEFAULT"
#BlockSize="nonDEFAULT"
DatasetSize="DEFAULT"
BlockSize="NON-DEFAULT"
preset="DEFAULT"
declare -a dynamic_vars=( DatasetSize BlockSize )
number_of_dynamic_vars="${#dynamic_vars[@]}"
number_of_default_values=0
overwrite_all() {
printf "all non-defaults variables\n"
fio_gen
}
overwrite_some() {
printf "some defaults variables\n"
for [[ "${!var}" == "$preset" ]]
do
# SOME LOGIC HERE TO replace non-default and call this function
fio_gen
done
}
overwrite_none() {
printf " all defaults variables\n"
vars_ioengine_defaults
fio_gen
}
check_vars() {
for var in "${dynamic_vars[@]}"
do
[[ "${!var}" == "$preset" ]] && ((number_of_default_values++))
done
if [[ "$number_of_default_values" -eq 0 ]]; then
echo "None var is default"
overwrite_all
elif [[ "$number_of_default_values" -eq "$number_of_dynamic_vars" ]]; then
echo "All defaults"
overwrite_none
else
echo "Mixed"
overwrite_some
echo $var
fi
}
vars_ioengine_defaults() {
RunTime="0"
UDCNAme="stage"
if [[ "$IOEnginge" == "psync" ]] && [[ "$TestType" == "read" ]]; then
QueueDepth="0"
DatasetSize="3G"
BlockSize="2,4,8,16,32,64,128,256,512,1024"
Threads="1,2,4,8,16,32,64,128,256"
FileSize="3M"
elif [[ "$IOEngine" == "psync" ]] && [[ "$TestType" == "randread" ]]; then
QueueDepth="0"
DatasetSize="1G"
BlockSize="8,16,32"
Threads="16,32,64,128,256"
FileSize="32k"
elif [[ "$IOEngine" == "libaio" ]] && [[ "$TestType" == "read" ]]; then
QueueDepth="16"
DatasetSize="3G"
BlockSize="2,4,8,16,32,64,128,256,512,1024"
Threads="1,2,4,8,16,32,64,128,256"
FileSize="3M"
elif [[ "$IOEngine" == "libaio" ]] && [[ "$TestType" == "randread" ]]; then
QueueDepth="16"
DatasetSize="1G"
BlockSize="8,16,32"
Threads="16,32,64,128,256"
FileSize="32k"
fi
}
fio_gen() {
echo "iteratre on -p $IOEngine -t $TestType -j $Threads -b $BlockSize -s $FileSize -d $DatasetSize -q $QueueDepth -r $RunTime -u $UDCName"
}
check_vars
答案1
这是实现以下步骤的解决方案的存根:
- 获取配置文件,其中所有默认值均在以
DFLT_
前缀命名的变量中定义。例如,DFLT_BlockSize
定义 的默认值BlockSize
。 - 对于每个默认变量(即名称以 开头的变量
DFLT_
)...- 获取实际变量的名称;
- 如果实际变量下的值为
DEFAULT
,则用默认变量的值替换。
让它成为我们的config.defaults
:
DFLT_BlockSize=5
DFLT_DatasetSize="1 2 3 infinity"
脚本如下:
#!/bin/bash
preset="DEFAULT"
#***Comment unComment to test 3 cases"
#DatasetSize="DEFAULT"
#BlockSize="DEFAULT"
#DatasetSize="nonDEFAULT"
#BlockSize="nonDEFAULT"
DatasetSize="DEFAULT"
BlockSize="NON-DEFAULT"
substitute_defaults() {
local dvar var
. "/path/to/config.defaults" # use the actual path to the config
for dvar in "${!DFLT_@}"; do
var="${dvar#DFLT_}"
declare -n var
[[ "$var" == "$preset" ]] && var="${!dvar}"
declare +n var
done
}
substitute_defaults
echo "$BlockSize"
echo "$DatasetSize"
解释:
"${!DFLT_@}"
返回以 开头的所有变量的名称。在循环DFLT_
的每次迭代中都会扩展为下一个这样的名称,例如。for
$dvar
DFLT_BlockSize
"${dvar#DFLT_}"
是删除"$dvar"
前导的DFLT_
。例如,如果$dvar
扩展为DFLT_BlockSize
则${dvar#DFLT_}
扩展为BlockSize
。这存储为var
。declare -n var
使对 的所有引用、赋值和属性修改var
(使用或更改-n
属性本身的修改除外)都对 的值所引用的变量执行var
。在我们的示例中($var
用于扩展为BlockSize
)从现在起的$var
行为类似于$BlockSize
并且var=
行为类似于BlockSize=
。${!dvar}
是类似的东西,无需 即可工作declare -n
:如果$dvar
扩展为 ,DFLT_BlockSize
则${!dvar}
其行为类似于$DFLT_BlockSize
。 (如果我们这样做declare -n dvar
,我们只需要$dvar
即可获得相同的效果)。因此当
$dvar
展开为时DFLT_BlockSize
,该行[[ "$var" == "$preset" ]] && var="${!dvar}"
表现得像
[[ "$BlockSize" == "$preset" ]] && BlockSize="$DFLT_BlockSize"
我们需要
declare +n var
(+n
撤销-n
)所以var="${dvar#DFLT_}"
在下一个循环中发生改变var
,而不是BlockSize
。
笔记:
- 我使用了一个单独的文件进行配置,只是为了将设置与代码分开。您可以
DFLT_
直接在脚本中定义这些变量(而不是. "/path/to/config.defaults"
)。 - 由于
config.defaults
是源代码,因此您应该将其视为脚本的一部分。特别是,它绝不限于设置变量;您在此文件中添加的任何代码都将像在脚本本身中一样执行。但这也意味着您可以在配置文件中添加注释;或条件语句!(以防万一:foo=bar rm -rf /precious/data
将运行rm
!foo="bar rm -rf /precious/data"
不会)。 - 您可能希望将默认变量保留在函数的本地。在这种情况下,配置文件中的条目应类似于
local DFLT_foo=bar
。我决定让示例文件保持简单,但local
似乎合理。 - 我的脚本不关心默认变量来自哪里,也不关心它们引用的实际变量(或位置参数!)。尝试使用
DFLT_1
影响$1
会导致错误,因为位置参数的行为与常规变量略有不同。
答案2
如果问题在于向 bash 脚本提供默认参数,那么我们的姊妹网站 Stack Overflow 上的帖子可以解答这一问题。
如果参数是位置参数,您将在帖子中找到几种解决方案 如何编写一个接受可选输入参数的 bash 脚本?。
这些解决方案使用参数扩展,定义在
Bash 参考手册 - 3.5.3 Shell 参数扩展,尤其是像这样的元素${1:-foo}
。
例如,您可以在脚本中添加:
ARG1=${1:-foo}
ARG2=${2:-bar}
ARG3=${4:-$(date)}
上述脚本要求缺失的参数始终是最后一个。
如果你更愿意在脚本中使用命名参数,本文介绍了一些方法 将命名参数传递给 shell 脚本。
举一个来自该帖子的例子,如果您使用单字母参数(例如在调用中)my_script -p '/some/path' -a5
,则可以使用类似的代码:
while getopts ":a:p:" opt; do
case $opt in
a) arg_1="$OPTARG"
;;
p) p_out="$OPTARG"
;;
\?) echo "Invalid option -$OPTARG" >&2
;;
esac
done