1

I have a shell function called "run", that normally just treats its parameters as a command line (i.e., as though 'run()' had not been used.) However, if the script is run in a $TESTMODE, 'run()' will echo its parameters, instead:

run ()
{
  cmd="$@"
  if [ -z "$TESTMODE" ]; then
    $cmd
  else
    echo "### RUN: '$cmd'"
  fi
}

The idea is, the command run rm -Rf / would normally attempt to delete your entire root filesystem. But, if $TESTMODE is defined, then it instead echos

### RUN: rm -Rf /

It works pretty well, until you attempt to use run() on a command-line that includes redirection:

run which which
run which bash >quiet

In which case, when $TESTMODE is defined, the desired echo will never be seen by human eyes:

$ rm quiet
$ TESTMODE=yes ./runtest.sh
### RUN: 'which which'
$ cat quiet
### RUN: 'which bash'

I attempted to correct for this with string replacement:

run ()
{
  cmd="$@"
  if [ -z "$TESTMODE" ]; then
    $cmd
  else
    cmd=${cmd//\>/\\>}
    cmd=${cmd//\</\\<}
    echo "### RUN: '$cmd'"
  fi
}

... which seemed promising in a command-line test:

$ test1="foo >bar"
$ echo $test1
foo >bar
$ test2=${test1//\>/\\>}
$ echo $test2
foo \>bar

... but failed miserably (no apparent change in behavior) from within my bash script.

Help? I'm sure this has something to do with quoting, but I'm not sure exactly how to address it.

4

2 に答える 2

2

逃げることではありません。リダイレクトは関数が開始する前に発生し、関数の制御外です。たとえば、bash が commandを認識する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[@]}") します。

出力リダイレクトの問題を回避する方法はいくつかあります。stdout の代わりに stderr に出力を送ることができます:

run ()
{
  if [ -z "$TESTMODE" ]; then
    "$@"
  else
    echo "### RUN: '$*'" >&2
  fi
}

...しかし、これにはいくつかの問題があります。リダイレクトを出力するのではなく実行しrun which bash >quietます(「quiet」という名前の空のファイルを作成し、「### RUN: 'which bash'」を出力します)。

また、stderr がリダイレクトされている場合は、端末ではなくそこに出力されます。これは、意図に応じて、良い場合も悪い場合もあります。別の可能性は、 を使用して端末に直接送信することecho "### RUN: '$*'" >/dev/ttyです。ここで考えられる問題: tty がないと失敗します (例: cron ジョブ)。

最後の注意: コマンドでは、コマンド全体を一連の文字列ではなく、スペースを含む単一の文字列として扱いたかったため、代わりechoに使用しました。これにより、出力が曖昧になります (例: vs. -- どちらも print ) が、コマンドを正しく実行することほど重要ではありません。明確なロギングが必要な場合は、次のようなより複雑なことを行う必要があります。$*$@run touch "foo bar"run touch "foo" "bar"### RUN: 'touch foo bar'

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

printf の%q形式は、コマンドとその引数に明確な (そしてシェルで実行可能な) 形式を使用します。

于 2013-01-16T04:06:03.873 に答える
2

実行する bash >quiet

これは STDOUT からの出力を無音にしますが、STDERR には無音にしません

だからあなたはこれを試すことができます

echo "### RUN: '$cmd'" >/dev/stderr

または単に

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

参照

于 2013-01-16T03:51:41.787 に答える