「bash -x」がこのスクリプトを壊すのはなぜですか?


13

コマンドの実行時間を測定するスクリプトがあります。

「bash-built-inにはフラグがないため」などの「実際の」timeコマンド、つまりバイナリが必要です。/usr/bin/time-f

以下に、デバッグ可能な簡略化されたスクリプトを示します。

#!/bin/bash

TIMESEC=$(echo blah | ( /usr/bin/time -f %e grep blah >/dev/null ) 2>&1 | awk -F. '{print $1}')

echo ABC--$TIMESEC--DEF

if [ "$TIMESEC" -eq 0 ] ; then
   echo "we are here!"
fi

「test.sh」として保存して実行します。

$ bash test.sh
ABC--0--DEF
we are here!

それでうまくいきました。

それでは、bashコマンドラインに「-x」を追加して、これをデバッグしてみましょう。

$ bash -x test.sh
++ echo blah
++ awk -F. '{print $1}'
+ TIMESEC='++ /usr/bin/time -f %e grep blah
0'
+ echo ABC--++ /usr/bin/time -f %e grep blah 0--DEF
ABC--++ /usr/bin/time -f %e grep blah 0--DEF
+ '[' '++ /usr/bin/time -f %e grep blah
0' -eq 0 ']'
test.sh: line 10: [: ++ /usr/bin/time -f %e grep blah
0: integer expression expected

「-x」を使用しているときにこのスクリプトが破損し、それがなくても正常に動作するのはなぜですか?


1
へえ。-xonでは、$()コンストラクトは-x結果の値の一部として出力を含めているように見えます。それが「予想される」動作なのかバグなのかはわからない...または、()実際に-x出力を提供しているのは内部のサブシェルかもしれません。
ジェフY

余談:設定を行うと、出力を問題の少ない場所にBASH_XTRACEFDリダイレクトできますset -x
チャールズダフィー

回答:


21

問題はこの行です:

TIMESEC=$(echo blah | ( /usr/bin/time -f %e grep blah >/dev/null ) 2>&1 | awk -F. '{print $1}')

標準出力を一致させるために標準エラーをリダイレクトする場所。bashは、トレースメッセージを標準エラーに書き込み、(たとえば)echoすべてをbashプロセスで他のシェル構成と一緒に使用しています。

それを何かに変更すると

TIMESEC=$(echo blah | sh -c "( /usr/bin/time -f %e grep blah >/dev/null )" 2>&1 | awk -F. '{print $1}')

その問題を回避し、おそらくトレースと動作の間の許容可能な妥協点になります。

++ awk -F. '{print $1}'
++ sh -c '( /usr/bin/time -f %e grep blah >/dev/null )'
++ echo blah
+ TIMESEC=0                 
+ echo ABC--0--DEF
ABC--0--DEF
+ '[' 0 -eq 0 ']'
+ echo 'we are here!'
we are here!

7

サブシェルをドロップすることもできます。どうやら互いに動揺するのはネストされたシェルです:

TIMESEC=$(
    echo blah |
    /usr/bin/time -f %e grep blah 2>&1 >/dev/null |
    awk -F. '{print $1}'
)

もしあなたがそうするなら:


...| ( subshell ) 2>pipe | ...

...サブシェルを起動して、サブシェルをホストするパイプラインのそのセクションを処理します。シェルは、サブシェルのデバッグ出力でさえ(使用することを選択した他の種類の{複合コマンドに対しても行う; } >redirectように) パイプラインのセクションにリダイレクトするため、ストリームをミキシングします。リダイレクトの順序に関係しています。

代わりに、ゲージを試行しているコマンドのエラー出力のみを最初にリダイレクトし、ホストシェルの出力をstderrにさせた場合、同じ問題が発生することはありません。

など...


... | command 2>pipe 1>/dev/null | ...

...ホストシェルは、パイプに呼び出すコマンドの出力のみをリダイレクトしつつ、必要に応じてstderrを書き続けることができます。


bash -x time.sh
+++ echo blah
+++ /usr/bin/time -f %e grep blah
+++ awk -F. '{print $1}'
++ TIMESEC=0
++ echo ABC--0--DEF
ABC--0--DEF
++ '[' 0 -eq 0 ']'
++ echo 'we are here!'
we are here!

そのことについては...


TIMESEC=$(
    echo blah |
    /usr/bin/time -f %e grep blah 2>&1 >/dev/null
)
printf %s\\n "ABC--${TIMESEC%%.*}--DEF"
if [ "${TIMESEC%%.*}" -eq 0 ] ; then
   echo "we are here!"
fi
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.