シェルスクリプトで「カレントディレクトリ以下全てを検索してマッチするファイルを圧縮」「圧縮形式をgzipからxz(など)に変更」

まえがき

数値計算結果など、大量のファイルを自動的に逐次圧縮したい、ということが自分はよくある。しかもそれらが複数のディレクトリにまたがっていて、「エクスプローラから右クリック」なんていちいちやっていられない、自動化したい、ということもよくある。そういう場合にはシェルスクリプトが便利。

bash(たぶん sh ならなんでも)を使うので LinuxMac, Win の場合は Cygwin などの利用を想定している。

ファイルを検索して圧縮

「カレントディレクトリ以下を全て検索し、拡張子が xyz のファイルを見つけて、それらに対して逐次 gzip -9 -q を実行」は、

$ find . -type f -name "*.func" -exec gzip -9 -q {} \; &

で行ける。xargs を使った方が速いんだとかそういう議論はあるようだが、よくわからない(検証もしてない)。

圧縮形式の変更

「すでにある .gz ファイルを .xz に圧縮し直す」になるともっと複雑。一度ディスクに書き出すのはまだわかるが、パイプを使って直接(つまりメモリのみ経由して?)xz にするのは全然わからない…が、エキスパートが提供してくれている:

pipe - How to convert all files from gzip to xz on the fly (and recursively)? - Unix & Linux Stack Exchange

念の為引用しておくと、Stéphane Chazelas さんによる最初の回答:

find . -name '*.gz' -type f -exec bash -o pipefail -Cc '
  for file do
    gunzip < "$file" | xz > "${file%.gz}.xz" && rm -f "$file"
  done' bash {} +

これで行けた。具体的には、一行目に #!/bin/bash をつけて gz2xz.sh などの名前で保存し、chmod で x 可能にすることで実行し、うまく作動することを確認した。

圧縮に関するコメント

圧縮形式については、gzip/bzip2/xz/zstd あたりのどれを使うか、というところは、どうやらファイルの種類によって最適なものが変わるようなので、たまに自分でテストしてみることをおすすめする。

ベンチマークでよく言われているのは、「bzip2 は圧縮率は gzip と xz の間くらいだが、解凍(展開)が遅い」ということで、確かにそういうケースもあったが、逆にあるファイル形式では「bzip2 が gzip よりも高圧縮で速度は同程度・xz と圧縮率がほぼ変わらず圧倒的に速い、かつ解凍も別に遅くない」ということがあった。オプションは、基本的にはデフォルトから変えても圧縮率は10%も変わらないかな、という印象(流石に-0とか-9みたいに極端に振ると結構変わることはあるが)。それよりも速度が結構変わることがあるので、そちらに注意したい。zstd はデフォルトの -3 だと異常に高速で圧縮率は gzip 並(かそれ以下かも)、というかなり速度重視感がある。急いでいるときは良いと思うけどまだ本格利用はしたことがない。

なお 2018-08現在、Cygwin をデフォルトでインストールすると xz と zstd は(bzip2もだったかも?)確か入ってこないので、追加する必要がある。軽いのでインストール自体はすぐ終わる。おそらく MacLinux でも xz はあっても zstd はないことが多そうなので、必要なら自分で入れる必要がある。