PIPESTATUSとbashスクリプトの出力の両方を取得する方法


9

このコマンドでファイルの最終更新日を取得しようとしています

TM_LOCAL=`ls -l --time-style=long-iso ~/.vimrc | awk '{ print $6" "$7 }'`

この行の実行後、TM_LOCALの値は「2012-05-16 23:18」のようになります。

また、PIPESTATUSをチェックして、エラーが発生したかどうかを確認したいと思います。たとえば、ファイルが存在しない場合lsは2を返しますが、戻り$?値はなので、値は0ですawk

このコマンドを単独で実行すると、lsの戻り値を確認できます。 ${PIPESTATUS[0]}

ls -l --time-style=long-iso ~/.vimrc | awk '{ print $6" "$7 }'

しかし$PIPESTATUS、最初の例のように出力を変数に割り当てた場合、期待どおりに動作しません。この場合、$PIPESTATUS配列には次と同じ1つの要素しかありません。$?

それで、問題は、どうすれば両方を取得$PIPESTATUSし、同時に出力を変数に割り当てることができるでしょうか?

回答:


8

あなたはこれを行うことができます:

TM_LOCAL=$(ls -l --time-style=long-iso ~/.vimrc | \
             awk '{ print $6" "$7 }' ; exit ${PIPESTATUS[0]} )

その後$?、からの戻りコードになりますls。これは、複数のパイプパーツからの戻りコードが必要な場合は機能しません(ただし、ここでのように出力が大きすぎない場合は、パイプラインを分割できます)。

以下は、PIPESTATUS配列全体と出力を取得するためのかなり高価な方法です。あまりエレガントではありませんが、他には何も見つかりませんでした:

result=$(echo -e "a\nb\nc" | \
          ( cat ; exit 1 ) | \
          ( cat ; exit 42 ) ; echo ${PIPESTATUS[@]})
output=$(head -n -1 <<< "$result")
status=($(tail -n 1 <<< "$result"))
echo "Output:"
echo "$output"
echo "Results:"
echo "${status[@]}"

それは与える:

Output:
a
b
c
Results:
0 1 42

私の場合はこれでうまくいきますが、完全なpipestatus配列と出力を取得する方法があるかどうかはまだ気になります。
Mustafa SerdarŞanlı12年

3

set -o pipefailin bashを使用して、パイプで連結されたコマンドシーケンスの右端のゼロ以外の終了コードをとして取得し$?ます。からman bash

設定されている場合、パイプラインの戻り値は、0以外のステータスで終了する最後(右端)のコマンドの値、またはパイプライン内のすべてのコマンドが正常に終了した場合は0です。このオプションはデフォルトで無効になっています。

その後、単ににアクセスできます$?set +o pipefail再び無効にするために使用します。


2

ここでの問題は、コマンドを実行するとすぐにPIPESTATUSが完全になくなることです。次の方法で、bashバージョン2以降で完全なPIPESTATUS配列を取得できます。

declare -a status
status=(${PIPESTATUS[@]})

次にアクセス${status[0]}${status[1]}など


2

「期待どおり」の主な問題は、逆引用符で囲まれたコマンドがサブシェルで実行されることです。$PIPESTATUSそこに存在し、そこから返されるステータスは、単一の実行可能ファイル(またはシェルスクリプト)を実行した場合と同じルールに従います。逆引用コマンドのステータスは、一番右(awk)のステータスです。

@ Daniel Beckの発言を実装pipefailするには、サブシェルでオプションを次のように設定します。

TM_LOCAL=`set -o pipefail; ls -l --time-style=long-iso ~/.vimrc | awk '{ print $6" "$7 }'` これで、$?後で格納されるステータスはls(ゼロ以外の場合)のステータスになります。

ただし、明示的なif [ -f ~/.vimrc ];...テストの方が読みやすいと思います。

PIPESTATUS前者の一時ファイルがないか、後者を文字列にマーシャリングしないと、変数に出力を取得して返すことはできません。


0

終了ステータスがゼロでない場合にのみ、cronからメールを送信したかった

トリックは、パイプラインの最後の標準入力を取得するには、サブシェルに配置する必要があることですが、PIPESTATUS値が非表示になっているようです...

テストcronは一部の出力を出力し、1または0で終了します。

./testcron | (test ${PIPESTATUS[0]} -ne 0 || mail -s "testcron output" paul)

更新:パイプラインコマンドが処理されるまで、PIPESTATUSは表示されません


0

1つのオプションは、を呼び出して変更時刻を取得する前に、ファイルの存在を確認することstatです。statはタイムスタンプで必要以上に返すため 、パラメータ展開を使用してトリミングできます。

GNU stat(例:Linux)では、次のコマンドを実行できます。

[[ -f ~/.vimrc ]] && TM_LOCAL=$(stat -c '%y' ~/.vimrc 2>/dev/null)
TM_LOCAL=${TM_LOCAL%:*}  # Safe to do, even if stat fails

Mac OS Xおよびその他のBSDシステムでは、stat構文が異なり、時刻形式を指定できます。

[[ -f ~/.vimrc ]] && TM_LOCAL=$(stat -f '%Sm' -t '%Y-%m-%d %H:%M' ~/.vimrc 2>/dev/null)

現在のGNUの答えは、への変更$TM_LOCALは安全であるとあなたは言います。以前の値を持たないと予想した場合にのみ安全です。値が以前のもの2020-02-27 17:14であり、~/.vimrcファイルがないとします。あなたはそれから持っているでしょう2020-02-27 17。したがって、これらの2行を追加&&または(できればそれほど読みにくいため)ifスタンザを使用してチェーンします。
アダムKatz
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.