今日は、AWS CLIを使っている時にひょっこり現れる [Errno 32] Broken pipe というエラーについて、自分用のメモとして残しておきます。
AWS CLIで遭遇する「Broken pipe」の正体
S3のファイル一覧を確認しようとして、headコマンドで件数を絞ったときに、こんなエラーが出たことはありませんか?
[Errno 32] Broken pipe
Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>
BrokenPipeError: [Errno 32] Broken pipe
なぜこのエラーが起きるの?
一言で言うと、「まだデータを送りたい送信側」と「もう十分だよ!と窓口を閉めた受信側」のすれ違いが原因です。
例えば、以下のコマンドを実行したとします。
aws s3 ls s3://my-bucket --profile my-profile | head -n 5
1.aws s3 ls は、バケット内のファイルをたくさん(例えば100個)見つけようとします。
2.一方で、パイプ(|)の先にいる head -n 5 は、「最初の5行だけ受け取ったら、もう仕事は終わり」と自分を終了させます。
3.aws s3 ls が6行目以降のデータを送ろうとしたとき、送り先の head がすでにいなくなっているため、「送り先(パイプ)が壊れているよ!」 という意味で Broken pipe が発生します。
このエラーは、データの整合性が壊れたわけではなく、あくまで「出力先のパイプが先に閉じられた」ことを知らせる通知のようなものです。
対処法1:エラー出力を捨てる
「原因はわかったし、動作に問題ないならエラーメッセージだけ消したいな」という場合は、標準エラー出力を /dev/null に逃がしてあげるのが一番シンプルですね。
(aws s3 ls s3://my-bucket --profile my-profile | head -n 5) 2>/dev/null
こうすることで、見た目上のエラーは表示されなくなります。
対処法2:S3 APIを使ってスマートに取得する
実は、aws s3 ls よりも aws s3api を使う方が、最初から取得件数を絞れるのでスマートだったりします。この方法なら、そもそも「余分なデータを送る」ことがないので、エラーも発生しません。
aws s3api list-objects-v2 \
--bucket <バケット名> \
--max-items 5 \
--profile <プロファイル名> \
--query 'Contents[].Key' \
--output text | tr '\t' '\n'
--max-items 5: 最初から5つだけ取得するようにリクエストします。
--query 'Contents[].Key': ファイル名(キー)だけを抽出します。
tr '\t' '\n': タブ区切りを改行に変換して、見やすく整形します。
まとめ
Broken pipe は、効率よく作業を終えようとした head と、一生懸命データを運ぼうとした aws のコミュニケーション不足から生まれる、ちょっと切ないエラーですね。
もし頻繁に見かけるようなら、API側で件数を絞る癖をつけておくと、ネットワークの帯域も節約できて一石二鳥かもしれません。
最後まで読んでいただき、ありがとうございました。