web-dev-qa-db-ja.com

コマンド置換から何も出力されませんか?

私はいくつかのシェルスクリプトを記述しましたが、この動作を見たことがなく、途方に暮れています。 bashシェルで実行される次の簡単なスクリプトがあります。

LOGFILE="/var/log/constructor-events.txt"

SUBSYSTEM="$1"
DEVTYPE="$2"
DEVICE="$3"

VENDOR=$(lsusb -D "$DEVICE" | grep idVendor 2>&1)

REQUEST=$(cat <<EOF
{"data": {
    "action": "add",
    "port": {
        "type": "$SUBSYSTEM",
    },
    "drive": {
        "vendor_id": "$VENDOR",
    }
}}
EOF
)

printf "output: $VENDOR" >> $LOGFILE
printf "%s\n" "`date +%x\ %r\ %Z` $REQUEST"  >> $LOGFILE

これはudevルールから実行されます。位置パラメーター(udevからのもの)には期待どおりの値があり、ログファイルに出力しても問題ありません。ただし、何らかの理由で、$VENDOR変数にはlsusbコマンドからの出力が含まれていません。

これが私が行ったデバッグです。

  • 発生している可能性のあるエラーをキャプチャするためにstdoutにstderrリダイレクトを追加しました。
  • 空のログに$VENDOR変数を直接送信する行を追加しました。
  • シェルで手動でスクリプトを実行し、$VENDOR変数をターミナルに出力しましたが、空です。
  • $DEVICE変数に含まれている文字列を取得し、シェルで直接実行すると、この結果が得られました。

    [root@Host ~]# lsusb -D /dev/bus/usb/016/030 | grep idVendor
    Cannot open /dev/bus/usb/016/030
    

ログファイルに出力するときに$VENDOR変数が空になるのはなぜですか?

編集:

これは、コマンド置換からリダイレクトを削除した後の更新です。

  • $VENDOR変数を端末に出力する行を追加しました。
  • $DEVICE変数を使用する代わりに、静的に値を割り当てました。

$VENDORをターミナルに出力するとエラーが出力されますが、それでもログファイルには追加されませんでした。ログファイルの$DEVICE ISの値です。コマンド置換内の出力リダイレクトと関係があると思います。

4
EternalHour

ここで、lsusbがエラーをドロップした場合、エラーメッセージはリダイレクトされず、grepからのエラーのみがリダイレクトされます。 lsusbからのエラーは、通常のstderr、つまりターミナル(またはスクリプトの開始時のstderrは何でも)に移動します。

_VENDOR=$(lsusb -D "$DEVICE" | grep idVendor 2>&1)
_

例えば。

_$ blah=$(ls -l /nonexisting | grep foo 2>&1)
ls: cannot access '/nonexisting': No such file or directory
$ echo "blah: '$blah'"
blah: ''
_

コマンドの置換でエラーもキャプチャするには、両方の出力をグループとしてリダイレクトする必要があります。

_$ blah=$( { ls -l /nonexisting | grep foo; } 2>&1)
$ echo "blah: '$blah'"
blah: 'ls: cannot access '/nonexisting': No such file or directory'
_

$( foo 2>&1 | grep 2>&1 )のような個別のリダイレクトを使用すると、最初のコマンドのエラーがパイプに送信され、grepでフィルター処理されます。上記の例では、空の出力が生成されます。

7
ilkkachu