根据变量的动态值构造脚本的参数

根据变量的动态值构造脚本的参数

团队,我有一组参数,它们在这个脚本 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

这是实现以下步骤的解决方案的存根:

  1. 获取配置文件,其中所有默认值均在以DFLT_前缀命名的变量中定义。例如,DFLT_BlockSize定义 的默认值BlockSize
  2. 对于每个默认变量(即名称以 开头的变量DFLT_)...
    1. 获取实际变量的名称;
    2. 如果实际变量下的值为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$dvarDFLT_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将运行rmfoo="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

相关内容