我有一个名为“run”的shell函数,它通常只将其参数视为命令行(即,好像'run()'没有被使用过。)但是,如果脚本是在{{}中运行的话。 1}},'run()'将回显其参数,而不是:
$TESTMODE
这个想法是,命令run ()
{
cmd="$@"
if [ -z "$TESTMODE" ]; then
$cmd
else
echo "### RUN: '$cmd'"
fi
}
通常会尝试删除整个根文件系统。但是,如果定义了$ TESTMODE,那么它将改为回声
run rm -Rf /
它很好用,直到你尝试在包含重定向的命令行上使用run():
### RUN: rm -Rf /
在这种情况下,定义run which which
run which bash >quiet
时,人眼永远不会看到所需的$TESTMODE
:
echo
我试图用字符串替换来纠正这个问题:
$ rm quiet
$ TESTMODE=yes ./runtest.sh
### RUN: 'which which'
$ cat quiet
### RUN: 'which bash'
......在命令行测试中似乎很有希望:
run ()
{
cmd="$@"
if [ -z "$TESTMODE" ]; then
$cmd
else
cmd=${cmd//\>/\\>}
cmd=${cmd//\</\\<}
echo "### RUN: '$cmd'"
fi
}
...但是在我的bash脚本中失败了(行为没有明显改变)。
帮助?我确定这与引用有关,但我不确定如何解决它。
答案 0 :(得分:2)
运行哪个bash&gt;安静
这使STDOUT的输出静音,但不是STDERR
所以你可以试试这个
echo "### RUN: '$cmd'" >/dev/stderr
或只是
echo "### RUN: '$cmd'" >&2
答案 1 :(得分:2)
这不是逃避的问题;重定向在函数开始之前发生,并且在函数控制之外。例如,当bash看到命令run which bash >quiet
时,bash首先将输出重定向到文件“quiet”,然后使用参数“which”和“bash”执行“run”函数。你的函数发送到stdout的任何东西都会自然地进入文件“quiet”。
您的脚本还有另一个问题。将命令存储在变量(cmd="$@"
)中时,它会丢失参数之间的中断。例如,您无法判断该命令是run touch "foo bar"
还是run touch "foo" "bar"
- 在任何一种情况下,$ cmd都设置为“touch foo bar”。这可能适用于打印命令,但由于您以该形式执行它,因此可能会造成麻烦。要解决此问题,请避免将其存储在变量中,或将其存储为数组(cmd=("$@")
;然后将其作为"${cmd[@]}"
执行)。
输出重定向问题有两种方法。您可以将输出发送到stderr而不是stdout:
run ()
{
if [ -z "$TESTMODE" ]; then
"$@"
else
echo "### RUN: '$*'" >&2
fi
}
...但是这有一些问题:它仍然执行重定向而不是打印它(run which bash >quiet
将创建一个名为“quiet”的空文件,然后打印“### RUN:'bash' “)。
此外,如果已重定向stderr,它将打印到该而不是终端。这可能是好的也可能是坏的,这取决于你的意图。另一种可能性是使用echo "### RUN: '$*'" >/dev/tty
直接发送到终端。可能存在的问题是:如果没有tty(例如在cron作业中),它将会失败。
最后注意:在echo
命令中,我使用$*
而不是$@
,因为我希望将整个命令视为带有空格而不是一系列字符串的单个字符串。这会使打印输出不明确(例如run touch "foo bar"
与run touch "foo" "bar"
- 两者都会打印### RUN: 'touch foo bar'
),但这并不像正确执行命令那么重要。如果你想要明确的日志记录,你必须做一些更复杂的事情:
echo "### RUN:" >&2
printf " %q" "$@" >&2
echo >&2
printf的%q
格式将对命令及其参数使用明确的(和shell可执行的)格式。