GNU tar は、アーカイブ中に対象のファイルが書き換わると file changed as we read it を出して終了コード 1 で終わります。アーカイブ自体はできていますが、$? != 0 で異常と判定すると、ログの圧縮などのジョブが止まります。この記事の出力は GNU tar 1.35(Ubuntu 26.04)で採りました。
まず見る(結論)
| 終了コード | 意味 | 実際に出た場面 |
|---|---|---|
| 0 | 正常 | — |
| 1 | 一部のファイルが読み込み中に変わった・消えた | file changed as we read it |
| 2 | 致命的なエラー | 対象が無い、読めない、書き込み先が無い、アーカイブが壊れている |
tar -czf "$ARCHIVE" "$DIR"
rc=$?
if [ $rc -eq 0 ]; then
echo "OK"
elif [ $rc -eq 1 ]; then
echo "WARN: ファイルが途中で変わった"
else
echo "ERROR (rc=$rc)"; exit $rc
fi
1 を正常扱いにするかどうかは、変わったファイルの中身が必要かで決めます。書き込み中のログを含めて固めるなら、1 は毎回起こりうる結果です。
読み込み中にファイルが変わる・消える
20MB のファイル3つを固めている途中で、別のプロセスから1つに追記しました。
$ tar -czf chg.tar.gz dir
tar: dir/f3: file changed as we read it
$ echo $?
1
$ tar -tzf chg.tar.gz
dir/
dir/f3
dir/f2
dir/f1
アーカイブは作られ、3つとも入っていました。
読み込み中にファイルを消した場合も 1 でした。消したファイルとディレクトリの両方について、file changed as we read it が出ました。
–warning=no-file-changed はメッセージだけを消す
$ tar --warning=no-file-changed -czf nw.tar.gz dir
$ echo $?
1
終了コードで判定しているスクリプトの対策にはなりません。判定は上のように 1 を分けて書きます。
終了コード 2 になる場面
| 場面 | メッセージ |
|---|---|
| 指定したファイルが無い | Cannot stat: No such file or directory と Exiting with failure status due to previous errors |
| 読めないファイルがある | Cannot open: Permission denied |
| 書き込み先のディレクトリが無い | tar (child): ...: Cannot open: No such file or directory |
| 展開するアーカイブが無い | tar (child): nosuch.tar.gz: Cannot open: No such file or directory |
| 途中で切れたアーカイブを読む | gzip: stdin: unexpected end of file / Unexpected EOF in archive |
指定したファイルが無いときも、ほかのファイルは固められていました(tar -czf missing.tar.gz dir nosuchfile は 2 で終わり、dir の中身は入っている)。終了コード 2 のときにできたファイルを正しいアーカイブとして扱わないよう、判定の後で消すか別名にします。
パイプでつなぐと失敗が見えない
$ tar -cf - dir nosuchfile | gzip > pipe.tar.gz
tar: nosuchfile: Cannot stat: No such file or directory
tar: Exiting with failure status due to previous errors
$ echo $? ${PIPESTATUS[*]}
0 2 0
bash でパイプの終了コードとして返るのは最後のコマンド(gzip)の 0 です。set -o pipefail を付けると、パイプ全体の終了コードが 2 になりました。set -e を付けていても、pipefail が無ければスクリプトは止まりません。
set -o pipefail
tar -cf - "$DIR" | gzip > "$ARCHIVE"
rc=$?
コメントを残す