大量のファイルを包んだ 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
コマンドにフォールバックされます。