如何在bash中有条件地抑制重定向?

如何在bash中有条件地抑制重定向?,bash,io-redirection,Bash,Io Redirection,我有一个名为“run”的shell函数,它通常只将其参数视为命令行(即,好像没有使用“run()”)。但是,如果脚本在$TESTMODE中运行,“run()”将回显其参数,而不是: run () { cmd="$@" if [ -z "$TESTMODE" ]; then $cmd else echo "### RUN: '$cmd'" fi } 其思想是,命令runrm-Rf/通常会尝试删除整个根文件系统。但是,如果定义了$TESTMODE,那么它将响应 ##

我有一个名为“run”的shell函数,它通常只将其参数视为命令行(即,好像没有使用“run()”)。但是,如果脚本在
$TESTMODE
中运行,“run()”将回显其参数,而不是:

run ()
{
  cmd="$@"
  if [ -z "$TESTMODE" ]; then
    $cmd
  else
    echo "### RUN: '$cmd'"
  fi
}
其思想是,命令
runrm-Rf/
通常会尝试删除整个根文件系统。但是,如果定义了$TESTMODE,那么它将响应

### RUN: rm -Rf /
在您尝试在包含重定向的命令行上使用run()之前,它工作得非常好:

run which which
run which bash >quiet
在这种情况下,当定义了
$TESTMODE
时,人眼将永远看不到所需的
回波

$ 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脚本中失败得很惨(行为没有明显的变化)

帮忙?我肯定这与引用有关,但我不确定具体如何解决

运行哪个bash>quiet

这会使标准输出静音,但不会使标准输出静音

所以你可以试试这个

echo“###RUN:'$cmd'>/dev/stderr
或者干脆

echo "### RUN: '$cmd'" >&2

运行哪个bash>quiet

这会使标准输出静音,但不会使标准输出静音

所以你可以试试这个

echo“###RUN:'$cmd'>/dev/stderr
或者干脆

echo "### RUN: '$cmd'" >&2

这不是逃避的问题;重定向发生在函数启动之前,超出了函数的控制范围。例如,当bash看到命令
runwhichbash>quiet
,bash首先将输出重定向到文件“quiet”,然后执行参数为“which”和“bash”的“run”函数。函数发送到stdout的任何内容都会自然地进入文件“quiet”

你的脚本还有另一个问题。将命令存储在变量(
cmd=“$@”
)中时,会松开参数之间的分隔符。例如,您无法判断命令是
运行touch“foo bar”
还是
运行touch“foo”“bar”
——在这两种情况下,$cmd都设置为“touch foo bar”。这对于打印命令来说可能没问题,但由于您是以这种形式执行它,因此可能会造成麻烦。要解决此问题,请避免将其存储在变量中,或将其存储为数组(
cmd=(“$@”)
;然后将其作为
“${cmd[@]}”
执行

有几种方法可以解决输出重定向问题。您可以将输出发送到stderr而不是stdout:

run ()
{
  if [ -z "$TESTMODE" ]; then
    "$@"
  else
    echo "### RUN: '$*'" >&2
  fi
}
…但这有几个问题:它仍然执行重定向而不是打印它(
runwhichbash>quiet
将创建一个名为“quiet”的空文件,然后打印“###run:‘which bash’”

此外,如果stderr已被重定向,它将打印到该服务器而不是终端。这可能是好的,也可能是坏的,这取决于你的意图。另一种可能是使用
echo“####RUN:'$*'”>/dev/tty
直接发送到终端。这里可能存在的问题:如果没有tty(例如,在cron作业中),它将失败

最后一点注意:在
echo
命令中,我使用了
$*
而不是
$@
,因为我希望整个命令被视为一个带空格的字符串,而不是一系列字符串。这使得打印输出不明确(例如,
运行touch“foo bar”
运行touch“foo”“bar”
——两者都会打印
###run:'touch foo bar'
),但这远不如正确执行命令重要。如果您想要明确的日志记录,则必须执行以下更复杂的操作:

echo "### RUN:" >&2
printf " %q" "$@" >&2
echo >&2

printf的
%q
格式将为命令及其参数使用明确的(和shell可执行的)格式。

这不是转义的问题;重定向发生在函数启动之前,超出了函数的控制范围。例如,当bash看到命令
runwhichbash>quiet
,bash首先将输出重定向到文件“quiet”,然后执行参数为“which”和“bash”的“run”函数。函数发送到stdout的任何内容都会自然地进入文件“quiet”

你的脚本还有另一个问题。将命令存储在变量(
cmd=“$@”
)中时,会松开参数之间的分隔符。例如,您无法判断命令是
运行touch“foo bar”
还是
运行touch“foo”“bar”
——在这两种情况下,$cmd都设置为“touch foo bar”。这对于打印命令来说可能没问题,但由于您是以这种形式执行它,因此可能会造成麻烦。要解决此问题,请避免将其存储在变量中,或将其存储为数组(
cmd=(“$@”)
;然后将其作为
“${cmd[@]}”
执行

有几种方法可以解决输出重定向问题。您可以将输出发送到stderr而不是stdout:

run ()
{
  if [ -z "$TESTMODE" ]; then
    "$@"
  else
    echo "### RUN: '$*'" >&2
  fi
}
…但这有几个问题:它仍然执行重定向而不是打印它(
runwhichbash>quiet
将创建一个名为“quiet”的空文件,然后打印“###run:‘which bash’”

此外,如果stderr已被重定向,它将打印到该服务器而不是终端。这可能是好的,也可能是坏的,这取决于你的意图。另一种可能是使用
echo“####RUN:'$*'”>/dev/tty
直接发送到终端。这里可能存在的问题:如果没有tty(例如,在cron作业中),它将失败

最后一点注意:在
echo
命令中,我使用了
$*
而不是
$@
,因为我希望整个命令被视为一个带空格的字符串,而不是一系列字符串。这使得打印输出不明确(例如,
运行touch“foo bar”
运行touch“foo”“bar”
——两者都会打印
###run:'touch foo bar'
),但这远不如正确执行命令重要。如果您想要明确的日志记录,则必须执行以下更复杂的操作:

echo "### RUN:" >&2
printf " %q" "$@" >&2
echo >&2
printf的
%q
格式将使用一种明确的(和shell可执行的)c语言格式