我想将参数传递给 dd 样式的 bash 脚本。基本上,我想要
./script a=1 b=43
Run Code Online (Sandbox Code Playgroud)
具有相同的效果
a=1 b=43 ./script
Run Code Online (Sandbox Code Playgroud)
我以为我可以通过以下方式实现这一目标:
for arg in "$@"; do
eval "$arg";
done
Run Code Online (Sandbox Code Playgroud)
确保eval
安全的好方法是什么,即"$arg"
匹配静态(无代码执行)变量赋值?
或者有没有更好的方法来做到这一点?(我想保持这个简单)。
ric*_*ici 16
您可以在 bash 中执行此操作而无需 eval(并且无需人工转义):
for arg in "$@"; do
if [[ $arg =~ ^[[:alpha:]_][[:alnum:]_]*= ]]; then
declare +i +a +A "$arg"
fi
done
Run Code Online (Sandbox Code Playgroud)
编辑:根据 Stéphane Chazelas 的评论,我在声明中添加了标志,以避免分配的变量已经被声明为数组或整数变量,这将避免在许多情况下declare
评估key=val
参数的值部分。(例如,+a
如果要设置的变量已经声明为数组变量,则会导致错误。)所有这些漏洞都与使用此语法重新分配现有(数组或整数)变量有关,这通常是众所周知的外壳变量。
事实上,这只是一类注入攻击的一个实例,它同样会影响eval
基于 -based 的解决方案:只允许已知的参数名称比盲目设置命令行中碰巧出现的任何变量要好得多。(例如,考虑如果命令行设置 会发生什么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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
称为 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
Run Code Online (Sandbox Code Playgroud)
lcd047 的解决方案使用硬编码DD_OPT_
前缀重构:
while [[ $1 =~ ^[[:alpha:]_][[:alnum:]_]*= ]]; do
eval "DD_OPT_${1%%=*}"='${1#*=}'; shift;
done
Run Code Online (Sandbox Code Playgroud)
frosschutz的大部分重构都值得称赞。
我把它放在一个带有全局变量的源文件中:
DD_OPTS_PARSE=$(cat <<'EOF'
while [[ $1 =~ ^[[:alpha:]_][[:alnum:]_]*= ]]; do
eval "DD_OPT_${1%%=*}"='${1#*=}'; shift;
done
EOF
)
Run Code Online (Sandbox Code Playgroud)
eval "$DD_OPTS_PARSE"
做所有的魔法。
函数的版本是:
DD_OPTS_PARSE_LOCAL="${PARSE_AND_REMOVE_DD_OPTS/DD_OPT_/local DD_OPT_}"
正在使用:
eval "$DD_OPTS_PARSE_LOCAL"
我用它制作了一个repo,包括测试和 README.md。然后我在我正在编写的 Github API CLI 包装器中使用它,并且我使用相同的包装器来设置所述repo的 github 克隆(引导很有趣)。
仅在一行中为 bash 脚本传递安全参数。享受。:)
支持经典的 Bourne shell,并且 Bash 和 Korn shell 仍然支持,这是一个-k
选项。当它生效时,dd
命令行上任何地方的任何“ -like”命令选项都会自动转换为传递给命令的环境变量:
$ set -k
$ echo a=1 b=2 c=3
$
Run Code Online (Sandbox Code Playgroud)
说服它们是环境变量有点困难;运行这个对我有用:
$ set -k
$ env | grep '^[a-z]=' # No environment a, b, c
$ bash -c 'echo "Args: $*" >&2; env' a=1 b=2 c=3 | grep '^[a-z]='
Args:
a=1
b=2
c=3
$ set +k
$ bash -c 'echo "Args: $*" >&2; env' a=1 b=2 c=3 | grep '^[a-z]='
Args: b=2 c=3
$
Run Code Online (Sandbox Code Playgroud)
第一个env | grep
演示没有使用单个小写字母的环境变量。第一个bash
显示没有传递给通过 执行的脚本的参数-c
,并且环境确实包含三个单字母变量。该set +k
取消-k
,也显示了相同的命令现在已经传递给它的参数。(a=1
被视为$0
脚本;您也可以通过适当的回声来证明这一点。)
这实现了问题的要求——打字./script.sh a=1 b=2
应该与打字相同a=1 b=2 ./script.sh
。
请注意,如果您在脚本中尝试这样的技巧,则会遇到问题:
if [ -z "$already_invoked_with_minus_k" ]
then set -k; exec "$0" "$@" already_invoked_with_minus_k=1
fi
Run Code Online (Sandbox Code Playgroud)
所述"$@"
被处理逐字; 不会重新分析以找到赋值样式的变量(在bash
和 中ksh
)。我试过:
#!/bin/bash
echo "BEFORE"
echo "Arguments:"
al "$@"
echo "Environment:"
env | grep -E '^([a-z]|already_invoked_with_minus_k)='
if [ -z "$already_invoked_with_minus_k" ]
then set -k; exec "$0" "$@" already_invoked_with_minus_k=1
fi
echo "AFTER"
echo "Arguments:"
al "$@"
echo "Environment:"
env | grep -E '^([a-z]|already_invoked_with_minus_k)='
unset already_invoked_with_minus_k
Run Code Online (Sandbox Code Playgroud)
并且仅already_invoked_with_minus_k
在exec
'd 脚本中设置了环境变量。
归档时间: |
|
查看次数: |
1277 次 |
最近记录: |