bash:変数を使用してstderr | stdoutリダイレクトを格納します


17

スクリプトにコマンドオプションを追加するような変数を介してstdoutとstderrをリダイレクトする方法はありますか?

たとえば、スクリプトがあります:

#!/bin/bash -x
TEST=">/dev/null 2>&1"
OPT='-p -v'
mkdir $OPT 123/123/123 $TEST

OPTは-p問題なく置き換えられ、bashはそれをオプションとして解釈することがわかります。ただし、リダイレクトはディレクトリ名として解釈されます。

$ ./test.sh 
+ TEST='>/dev/null 2>&1'
+ OPT='-p -v'
+ mkdir -p -v 123/123/123 '>/dev/null' '2>&1'
mkdir: created directory `123/123'
mkdir: created directory `123/123/123'
mkdir: created directory `>/dev'
mkdir: created directory `>/dev/null'
mkdir: created directory `2>&1'

bashと言う方法はありますか。$ VARはリダイレクト名であり、dirs名ではありません。

PS。間違った方向に進んでいる可能性がありますが、スクリプトからオプションの冗長または非冗長出力を作成したいです。しかし、非冗長モードでも出力が必要なので、スクリプト内の一部のコマンドからのみ、stdoutとstderr全体をリダイレクトすることはできません。

回答:


18

別の解決策は次のとおりです。

#!/bin/bash

verbose=0

exec 3>&1
exec 4>&2

if ((verbose)); then
  echo "verbose=1"
else
  echo "verbose=0"
  exec 1>/dev/null
  exec 2>/dev/null
fi

echo "this should be seen if verbose"
echo "this should always be seen" 1>&3 2>&4

次に1>&3 2>&4、出力を表示するコマンドにのみ追加します。


すごい。これはまさに私が見たものです。ありがとうございました。
ラッシュ

これがどのように機能するかを説明する記事を書きまし
transang

5

「方法」は、他の回答で十分に説明されています。OPのコードが機能しない「なぜ」に対処したい。

キーノートは、出力リダイレクトが変数展開の前にマークされることです。 リダイレクトは、実際に変数の展開後に実行されます(そのため、出力を変数に保存されているファイル名にリダイレクトできるため)が、シェル、変数が展開される前に後の処理のためにリダイレクトを識別します。

言い換えると、シェルはコマンド文字列のどの部分をリダイレクトとして処理するかをすでに識別しているため、変数が展開されると、リダイレクト文字(<または>など)が考慮されるのは「遅すぎます」。

詳細については、以下にリストされているステップ1および3を参照してください。

LESS='+/^SIMPLE COMMAND EXPANSION' man bash

3

これは「ディレクトリ名」として解釈されず、>引用されているため、文字どおりに処理されます(具体的には、文字列を送信しています>dev/null 2>&1。これを回避する唯一の方法はeval、新しいシェルを使用または生成することです。

あなたの質問で暗示されている「冗長な」問題については、代わりにこれをしてください:

verbose=1
if (( verbose )); then
    mkdir -v -p /foo
else
    mkdir -p /foo > /dev/null 2>&1
fi

1

変数に多くのスペースがあり、適切に評価されません。あなたはそれをevalセットアップするのに使いたいでしょう。

#!/bin/bash -x
TEST=">/dev/null 2>&1"
OPT='-p -v'
eval mkdir $OPT 123/123/123 $TEST

これにより、$ OPTを1つ()ではなく2つの引数(-pおよび-v)に分割でき-p -v、$ TESTでも同じです。また、現在のディレクトリにディレクトリ/dev/nullがある可能性は非常に低いため、使用するように変更されdevました。


0

enzotibの答えはいくつかの優れたファイル記述子マジックを使用しますが、より簡単な解決策は次evalの行を使用することです(ただし、プロセスのオーバーヘッド追加されます)。

$ rm /tmp/foo
$ ll /tmp/foo
ls: cannot access /tmp/foo: No such file or directory
$ FOO=">/tmp/foo"
$ date $FOO
date: invalid date '>/tmp/foo'
$ cat /tmp/foo
cat: /tmp/foo: No such file or directory
$ eval date $FOO
$ cat /tmp/foo
Thu Dec 13 14:08:17 EST 2018
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.