サーバーにカスタムファイアウォールルールがあるかどうかをテストしようとしています。 awkを使用してiptables出力を処理し、適切な終了コードを返すことでこれを行います。これは期待どおりに機能し、正しい結果を印刷します。ただし、ティーにパイプで接続すると、条件は機能しなくなり、毎回結果が印刷されます。
# The broken code
ssh server1 'sudo iptables -L | awk "(!/ACCEPT/ && !/^target/ && !/^$/) {rules=1 ;} (rules==1) {exit 1}" && printf "%s\n" "string1" "string2" | sudo tee -a /path/to/file'
# The same code without `| sudo tee -a /path/to/file` works as intended
ssh server1 'sudo iptables -L | awk "(!/ACCEPT/ && !/^target/ && !/^$/) {rules=1 ;} (rules==1) {exit 1}" && printf "%s\n" "string1" "string2"'
私は何を見逃していますか?私はRHEL8でGNU coretils 8.30とGNU bashバージョン4.4.20(1)を使用しています。私はawkのドアが短くなる可能性があることを知っていましたが、それはポイントではありません。 :D
答え1
あなたの苦情は主にパイプから返された状態に関するものですiptables ... | awk ... && printf ... | tee ...
。飼育剤文書
その価値が何であるかを説明してください。
パイプライン障害オプションが有効になっていない場合、パイプラインの戻り状態は最後のコマンドの終了状態です。 Pipefailが有効な場合、パイプラインの戻り状態は、ゼロ以外の状態で終了した最後の(最も右側の)コマンドの値、またはすべてのコマンドが正常に終了した場合は0です。
実行を検討してください
set -o pipefail
パイプラインの結果前。
あるいは、より簡単な場合は、... | (cmd ... || true) | ...
イディオムを使用して各ステップを返すことができます。0
例外が検出されたら、それ以降のパイプライン段階でgrep
それを処理できるように固有のメッセージをエクスポートします。
bashにawkの単純処理とprintfの単純処理を条件付きで調整させる設計決定は、当時は合理的だったかもしれませんが、今はこれが生産性を低下させることがわかります。このロジックを単純なawkスクリプトに統合することを検討してください。
短いパイプを使用する方が便利かもしれません。
ssh server1 ... | awk ... > server1.txt
# awk exit status is now available in $? so you can conditionally run printf
cat server1.txt >> /path/to/file
もともとの質問では、/path/to/file はサーバーパス名であり、私たちは少し複雑な一行のリモートパイプラインを実行していました。iptables -L
SSH接続を介して生の出力を返し、ローカルパイプラインを実行する方が便利です。ローカル結果ファイルを作成したら、いつでも次のように保存できます。
cat results.txt | ssh server1 'cat >> /path/to/file'