大量のファイルを包んだ zip ファイルを unzipコマンドで展開すると,大量のファイル名がズラズラと表示され,その出力処理のオーバーヘッドで,時間が無用にかかってしまいます。
$ unzip LargeArchive.zip -d DestDir
Archive: LargeArchive.zip
creating: ...
(以下大量にファイル名表示の行が続く)➡ 時間が無用にかかる!
unzip に -q オプション(quiet)を渡せば出力を消して黙らせることができますが,すると今度は展開処理中に反応がなく止まってしまい,あたかもフリーズしたように見えてしまいます。
理想は,進捗状況を表すプログレスバーが表示されることですが,残念ながら unzip の標準機能ではそのような機能は用意されていません。そこで,Pipe Viewer(pvコマンド)を噛ませることで,プログレスバー表示の unzip を実現しましょう。
pv コマンドのインストール
pv コマンド(pipe viewer)を,何らかの方法でインストールします。
Homebrew
brew install pv
MacPorts
sudo port install pv
ソースから (configure & make)
公式サイトに configure & makeによるソースコードからのビルド手順の解説があります。
Universal Binary としてビルドする手順は次の記事に記しました。
pv & unzip のラッパーとなるシェル関数を定義
pv_unzip というシェル関数を,次のように定義します。
function pv_unzip() {
local unzip_opts=()
local zipfile=""
local outdir=""
# 引数を解析
while [[ $# -gt 0 ]]; do
case "$1" in
-*)
unzip_opts+=("$1")
shift
;;
*)
if [[ -z "$zipfile" ]]; then
zipfile="$1"
elif [[ -z "$outdir" ]]; then
outdir="$1"
else
echo "Too many arguments" >&2
return 1
fi
shift
;;
esac
done
# 引数チェック
if [[ -z "$zipfile" ]]; then
echo "Usage: pv_unzip [unzip options] zipfile.zip [output_dir]" >&2
return 1
fi
# outdir が未指定の場合はカレントディレクトリを使用
if [[ -z "$outdir" ]]; then
outdir="."
fi
# ファイルとディレクトリの存在チェック
if [[ ! -f "$zipfile" ]]; then
echo "Error: '$zipfile' does not exist or is not a file" >&2
return 1
fi
if [[ ! -d "$outdir" ]]; then
mkdir -p "$outdir" || { echo "Error: Cannot create '$outdir'" >&2; return 1; }
fi
# pv の存在確認と処理の分岐
if command -v pv >/dev/null 2>&1; then
# pv がある場合:進捗表示付きで展開
local total_files
total_files=$(unzip -l "$zipfile" | sed -n '/-----/,/-----/p' | sed '1d;$d' | wc -l)
unzip "${unzip_opts[@]}" -d "$outdir" "$zipfile" | pv -l -s "$total_files" > /dev/null
local unzip_status=${PIPESTATUS[0]} # unzip の終了ステータスを取得
return "$unzip_status"
else
# pv がない場合:通常の unzip で展開
unzip "${unzip_opts[@]}" -d "$outdir" "$zipfile"
return $? # unzip の終了ステータスをそのまま返す
fi
}
この中で,
total_files=$(unzip -l "$zipfile" | sed -n '/-----/,/-----/p' | sed '1d;$d' | wc -l)
の部分は,zipアーカイブ内の総ファイル数を取得しています。ヘッダ行などファイル名以外の部分を除外するために,----- で囲まれたファイルリスト部分を取り出し,最初の1行(-----)と最後の1行(-----)を削除(sed '1d;$d')して,その行数をカウント(wc -l)することで,zipアーカイブ内の総ファイル数を調べているわけです。
使い方
pv_unzip [unzip options] ARCHIVE.zip [output_dir]
-
-o(overwrite) のようなオプションを指定すると,それがそのままunzipコマンドに伝えられます。 - ただし,本来の
unzipコマンドにある-d(出力先ディレクトリ)オプションは不要で,第2引数に出力先ディレクトリを指定します。 - 第2引数(出力先ディレクトリ)の指定を省略した場合は,カレントディレクトリに展開されます。
-
pvコマンドがインストールされていない場合は,普通のunzipコマンドにフォールバックされます。
