我有一个脚本,除了执行位置参数之外什么也没做。 (我意识到安全风险,并且该脚本没有任何用处,因为它是一个最小的工作示例。)
$ cat script
> #!/usr/bin/env bash
>
> eval "${*}"
$ cat "docu ment"
> Lorem ipsum dolor sit amet
我想做的是用./script cat "docu ment"
, 或调用脚本./script cat docu\ ment
,但是引号或转义字符消失,脚本将尝试cat docu ment
,但这不起作用。在这种情况下我该如何修复引用?
编辑:我真正想做的是多次调用命令,直到它返回成功的退出代码,或者尝试了 n 次。我的脚本如下所示:
#!/usr/bin/env bash
# Try command several times, until it reports success (exit code 0)
# or I give up (tried n times)
tryMax=10
try=1
# Do until loop in bash
while
eval "${@}"
exitcode="${?}"
[[ "${exitcode}" -ne 0 && "${try}" -lt "${tryMax}" ]]
do (( try++ ))
done
if [[ "${exitcode}" -ne 0 ]]; then
echo -n "I tried hard, but did not manage to make this work. The exit code "
echo "of the last iteration of this command was: ${exitcode}."
exit "${exitcode}"
fi
答案1
你不需要eval
这里。你可以只使用"$@"
:
while
"${@}"
exitcode="${?}"
[[ "${exitcode}" -ne 0 && "${try}" -lt "${tryMax}" ]]
do ...
"$@"
将扩展为脚本的所有参数作为单独的“单词”- 尊重阻止的原始引用分词- 进而让您使用第一个参数作为等待运行的命令( cat
),其余参数作为cat
( docu ment
) 的参数。
这不起作用的地方:
"$@"
如果您希望传入的命令使用其他更高级别的 shell 结构,例如管道、函数定义、循环等。这些都会在参数扩展之前处理,并且在扩展后不会再次尝试。- 如果命令的返回码被否定
! cmd
。!
也在处理命令开始时、参数扩展之前进行处理。 - 如果命令是多个命令
x \; y
or 或$'x\ny'
,或者与orx $'\n' y
相同。所有这些都只是常规争论。&&
||
- 如果您的命令前面有变量赋值,例如
LD_LIBRARY_PATH=/x foo
.您可以将它们放在脚本名称之前,但不能放在参数命令之前。 - 如果命令中有重定向
>foo
,则3<bar
在其中。这些可能会或可能不会附加到脚本,因为脚本有自己的日志输出。 - 如果命令有此处文档或此处字符串。如果附加到脚本本身,这些内容只能读取一次,因此具体取决于命令失败的具体时间,您可能会或可能不会没事。无论如何,这些都很难适当地传递
eval
。 - 如果命令是子 shell
( ... )
或命令组{ ... ; }
。这些将被视为称为 和 的命令,(
而{
不是语法结构。 - 如果命令包含命令替换
$(...)
需要重复运行。您可以使用它(或任何其他 shell 构造)来生成原始参数,但一旦脚本开始运行,它们都将是固定字符串。 - 如果命令有任何其他应重复评估的元素,例如
$RANDOM
或一个算术展开$((i++))
。 - 如果命令是
time
.这是 shell 保留字,并且不是内置命令,因此也在参数扩展之前进行处理。
然而,否则,您可以成功地eval
完全避免,并且应该这样做。即使忽略任何可能的安全问题,正确构建它也非常脆弱。