bash 脚本的 dd 样式参数

bash 脚本的 dd 样式参数

我想将参数传递给 dd 风格的 bash 脚本。基本上,我想要

./script a=1 b=43

具有相同的效果

a=1 b=43 ./script

我想我可以通过以下方式实现这一目标:

for arg in "$@"; do
   eval "$arg";
done

确保安全eval(即"$arg"匹配静态(无代码执行))变量赋值的好方法是什么?

或者有更好的方法来做到这一点吗? (我想保持简单)。

答案1

您可以在 bash 中执行此操作,无需 eval(并且无需人工转义):

for arg in "$@"; do
  if [[ $arg =~ ^[[:alpha:]_][[:alnum:]_]*= ]]; then
    declare +i +a +A "$arg"
  fi
done

编辑:根据 Stéphane Chazelas 的评论,我在声明中添加了标志,以避免分配的变量已声明为数组或整数变量,这将避免在许多情况下declare评估key=val参数的值部分。 (+a例如,如果要设置的变量已声明为数组变量,则会导致错误。)所有这些漏洞都与使用此语法重新分配现有(数组或整数)变量有关,这通常是众所周知的外壳变量。

事实上,这只是一类注入攻击的一个实例,它同样会影响eval基于 的解决方案:只允许已知的参数名称比盲目设置命令行中出现的任何变量要好得多。 (PATH例如,考虑一下如果命令行设置 会发生什么。或者重置PS1以包含一些将在下一个提示显示时发生的评估。)

我更愿意使用命名参数的关联数组,而不是使用 bash 变量,这既更容易设置,也更安全。或者,它可以设置实际的 bash 变量,但前提是它们的名称位于合法参数的关联数组中。

作为后一种方法的示例:

# Could use this array for default values, too.
declare -A options=([bs]= [if]= [of]=)
for arg in "$@"; do
  # Make sure that it is an assignment.
  # -v is not an option for many bash versions
  if [[ $arg =~ ^[[:alpha:]_][[:alnum:]_]*= &&
        ${options[${arg%%=*}]+ok} == ok ]]; then
    declare "$arg"
    # or, to put it into the options array
    # options[${arg%%=*}]=${arg#*=}
  fi
done

答案2

POSIX 一个(设置$<prefix>var而不是$var避免特殊变量如IFS/ PATH... 的问题):

prefix=my_prefix_
for var do
  case $var in
    (*=*)
       case ${var%%=*} in
         "" | *[!abcdefghijiklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_]*) ;;
         (*) eval "$prefix${var%%=*}="'${var#*=}'
       esac
  esac
done

称为 as myscript x=1 PATH=/tmp/evil %=3 blah '=foo' 1=2,它将分配:

my_prefix_x <= 1
my_prefix_PATH <= /tmp/evil
my_prefix_1 <= 2

答案3

lcd047 的解决方案用硬编码前缀重构DD_OPT_

while [[ $1 =~ ^[[:alpha:]_][[:alnum:]_]*= ]]; do
  eval "DD_OPT_${1%%=*}"='${1#*=}'; shift;
done

弗罗斯特舒茨大部分重构都值得赞扬。

我将其作为全局变量放入源文件中:

DD_OPTS_PARSE=$(cat <<'EOF'
  while [[ $1 =~ ^[[:alpha:]_][[:alnum:]_]*= ]]; do
    eval "DD_OPT_${1%%=*}"='${1#*=}'; shift;
  done
EOF
)

eval "$DD_OPTS_PARSE"发挥所有魔力。

函数的版本是:

DD_OPTS_PARSE_LOCAL="${PARSE_AND_REMOVE_DD_OPTS/DD_OPT_/local DD_OPT_}"

正在使用:

eval "$DD_OPTS_PARSE_LOCAL"

我制造了一个回购协议除此之外,还有测试和 README.md。然后我在我正在编写的 Github API CLI 包装器中使用了它,并且我使用相同的包装器来设置上述的 github 克隆回购协议(引导很有趣)。

只需一行即可安全传递 bash 脚本的参数。享受。 :)

答案4

我的尝试:

#! /usr/bin/env bash
name='^[a-zA-Z][a-zA-Z0-9_]*$'
count=0
for arg in "$@"; do
    case "$arg" in
        *=*)
            key=${arg%%=*}
            val=${arg#*=}

            [[ "$key" =~ $name ]] && { let count++; eval "$key"=\$val; } || break

            # show time
            if [[ "$key" =~ $name ]]; then
                eval "out=\${$key}"
                printf '|%s| <-- |%s|\n' "$key" "$out"
            fi
            ;;
        *)
            break
            ;;
    esac
done
shift $count

# show time again   
printf 'arg: |%s|\n' "$@"

它可以处理 RHS 上的(几乎)任意垃圾:

$ ./assign.sh Foo_Bar33='1 2;3`4"5~6!7@8#9$0 1%2^3&4*5(6)7-8=9+0' '1 2;3`4"5~6!7@8#9$0 1%2^3&4*5(6)7-8=9+0=33'
|Foo_Bar33| <-- |1 2;3`4"5~6!7@8#9$0 1%2^3&4*5(6)7-8=9+0|
arg: |1 2;3`4"5~6!7@8#9$0 1%2^3&4*5(6)7-8=9+0=33|

$ ./assign.sh a=1 b=2 c d=4
|a| <-- |1|
|b| <-- |2|
arg: |c|
arg: |d=4|

相关内容