sh: 如果命令仅包含字母、数字和下划线,那么将变量用作命令是否安全?

sh: Is it safe to use a variable as a command if the command contains only letters, number and underscores?

我正在用 dash 编写一个 POSIX 兼容的脚本,所以我必须通过使用假数组来发挥创意。

fake_array.sh

的内容
fake_array_job() {
array=""
job_name=""

comma_count="$(echo "$array" | grep -o -F ',' | wc -l)"

if [ "$comma_count" -lt '1' ]; then
    echo 'You gave a fake array to fake_array_job that does not contain at least one comma. Exiting...'
    exit
fi

array_count="$(( comma_count + 1 ))"

position=1
while [ "$position" -le "$array_count" ]; do
    item="$(echo "$array" | cut -d ',' -f "$position")"

    "$job_name" || exit

    position="$(( position + 1 ))"
done
}

script.sh

的内容
#!/bin/sh

. fake_array.sh

job_to_do() {
    echo "$item"
}
fake_array_job 'goat,pig,sheep' 'job_to_do'

second_job() {
    echo "$item"
}
fake_array_job 'apple,orange' 'second_job'

我知道为我传递给 fake_array_job 的每个作业使用唯一的名称可能看起来很愚蠢,但我喜欢我必须输入两次,因为它有助于减少人为错误。

我一直在读到将变量用作命令是个坏主意。我对 运行 函数使用“$job_name”是否有任何负面影响,因为它涉及稳定性、安全性或效率?

(读到最后,Charles Duffy 提出了一个很好的建议。我懒得完全重写我的答案以提早提...)


您可以使用简单的参数扩展遍历 "array",而不需要数组中有多个元素。

fake_array_job() {
    args=${1%,},   # Ensure the array ends with a comma
    job_name=

    while [ -n "$args" ]; do
        item=${args%%,*}
        "$job_name" || exit
        args=${args#*,}
    done 
}

上面的一个问题是,通过假设 foo,bar, 不是最后一个元素为空的 comma-delimited 数组来确保数组是 comma-terminated。更好(虽然更难看)的解决方案是使用 read 来分解数组。

fake_array_job () {
  args=
  job_name=
  rest=$args
  while [ -n "$rest" ]; do
    IFS=, read -r item rest <<EOF
$rest
EOF
    "$job_name" || exit
  done  
}

(您可以使用 <<-EOF 并确保此处的文档使用 制表符 缩进,但很难在此处表达,所以我将保留丑陋的版本。)

还有 Charles Duffy 的好建议,使用 case 对数组进行模式匹配,看看是否还有逗号:

while [ -n "$args" ]; do
  case $var in 
    *,*) next=${args%%,*}; var=${args#*,}; "$cmd" "$next";;
      *) "$cmd" "$var"; break;; 
  esac;
done