Edited at

Fabricではシェルスクリプト中のパイプの途中でのステータスコードは取得出来ないという話

More than 3 years have passed since last update.


背景

たとえば、

$ true | false | true

$ ls -S1 hoge* | haed -n1

の様なパイプ処理を含んだシェルスクリプトを動作させた直後に、

$ echo $?

などのコマンドでステータスコードを確認したとき、最初や途中の処理でエラーが発生していたとしても、パイプの最後のステータスコードしか取得出来ない。

途中経過のステータスコードを取得する際には、実行直後に、${PIPESTATUS[@]}という配列を参照することで得ることが出来る。

$ true | false | true

$ echo ${PIPESTATUS[@]}
0 1 0


Fabric の場合

通常、Fabric の場合には、runコマンドや sudoコマンドなどの戻り値に.succeeded.failedなどというプロパティがあるので、これを参照することが多い。

result = run("false", warn_only=True)

print result.succeeded

ただし、${PIPESTATUS[@]}に相当する変数はないため、途中でのステータスコードは取得出来ない。


問題点

冒頭のシェルスクリプトの様な例を考える。

@task

def return_test():
# カレントディレクトリの hoge という文字列を含むファイルのなかで一番大きいものを表示する
result = run("ls -S1 *hoge* | head -n1")
print result.succeed
print result

このときに、カレントディレクトリに hoge を含むファイル/ディレクトリがない場合には、.succeededrunの結果しか返しないし(headは実行出来ているので、ここはTrue)、内容としてはエラーの表示(標準エラー出力)がそのまま返ってしまう。

[192.168.0.1] Executing task 'return_test'

[192.168.0.1] run: ls -S1 *hoge* | head -n1
[192.168.0.1] out: ls: cannot access *hoge*: No such file or directory
[192.168.0.1] out:

True
ls: cannot access *hoge*: No such file or directory

Done.
Disconnecting from 192.168.0.1... done.

本来欲しい情報が返っているわけではないし、気持ち悪いのだけど、この例だとたまたまそういうディレクトリ名なのかも知れない場合もあるし困る。


解決法(結論)

お茶を濁しましょう。

@task

def return_test():
result = run("ls -S1 *hoge* 2> /dev/null | head -n1")
print result

[192.168.0.1] Executing task 'return_test'

[192.168.0.1] run: ls -S1 *hoge* 2> /dev/null | head -n1

Done.
Disconnecting from 192.168.0.1... done.