アーカイブコマンドの使い方

macOS における色々あって忘れがちなアーカイブコマンドの使い方を記しておく。

## セットアップ

Homebrew で次のコマンドを使ってインストールする。

```sh
brew install \
	7-zip p7zip zip libarchive gnu-tar cpio lha gzip bzip2 xz lzip lrzip rzip lz4 lzop brotli zstd lzfse wimlib rar zpaq
```

* tar/cpio について
	* `gnu-tar` と `cpio` によりGNU版tar/cpioがインストールされる。  `gnu-tar` は `gtar` としてリンクされる。 `cpio` はリンクされていないので、手動でリンクする。
	* `libarchive` によりBSD版tar/cpioがインストールされる。 `bsdtar`, `bsdcpio` コマンドがあるが共にリンクされていないので、手動でリンクする。
	* macOS には標準でBSD版が付いてくる。

* 7-zip について
	* p7zip と 7-zip の2種類のユーティリティがあるみたい
	* ただ、それぞれでコマンドが違う
		* `brew install p7zip` → `7z` コマンド
		* `brew install 7-zip` → `7zz` コマンド
	* ここではバージョンがより新しく更新されている `7zz` コマンドを使う。ただ、ドキュメントに関しては p7zip の方が豊富。

### Debian Linux
```sh
apt install \
	zpaq gzip pigz bzip2 pbzip2 xz-utils pixz lzip plzip lrzip lz4 lzop rzip zstd brotli libarchive-tools tar zip p7zip rar cpio wimtools genisoimage
```

<br>

## アーカイブ/圧縮

### ファイル/ディレクトリから新規アーカイブを作成

#### zip

```sh
zip -q -r -x ".*" -x "__MACOSX" archive.zip file1.txt file2.txt ...
# 或いは
ditto -c -k --norsrc --noextattr --noqtn --noacl \
	file1.txt file2.txt ... archive.zip
```

* `zip` ではアーカイブ内のディレクトリ構造は指定したパス通りに作ってしまうので、カレントディレクトリにないファイルをアーカイブするのであれば、そのファイルが存在するディレクトリにカレントディレクトリを移動させる必要がある
* リソースフォークを含まないようにするため
	* `zip` では `-x "*." -x "__MACOSX"` により除外ファイルを具体的に指定
	* `ditto` では `--norsrc --noextattr --noqtn --noacl` を付加
* `zip` でディレクトリをアーカイブするには `-r` (Recursive) オプションを付加する
* `zip` で出力を抑制するために `-q` オプションを付加

#### tar

```sh
COPYFILE_DISABLE=1 tar -c -f archive.tar file1.txt file2.txt ...
```
* Mac のシステムリソースを含まないようにするため `COPYFILE_DISABLE` 環境変数を `1` に指定する

#### 7-zip

```sh
7zz a -ba -t7z -xr'!.*' archive.7z  file1.txt file2.txt ...
```

* 7z で出力を抑制するために `-ba` オプションを付加
* Mac のシステムリソースを含まないようにするため `-xr'!.*'` により除外ファイルを具体的に指定

#### rar

```sh
rar a -inul -r archive.rar file1.txt file2.txt ...
```

#### lha

```sh
lha -a -q archive.lzh file1.txt file2.txt ...
```
* 出力を抑制するために `-q` オプションを付加

#### pax

```sh
COPYFILE_DISABLE=1 pax -w -f archive.pax file1.txt file2.txt ...
```

#### cpio

```sh
printf "file1.txt\0file2.txt" | cpio -o -0 -O archive.cpio

# 或いは
ditto -c    file1.txt file2.txt archive.cpio
ditto -c -z file1.txt file2.txt archive.cpio # gzip 圧縮
ditto -c -j file1.txt file2.txt archive.cpio # bzip2 圧縮
```
* `cpio` は `-0` オプションを付すことで区切り文字をヌル文字にする

#### zpaq

```sh
zpaq a archive.zpaq file1.txt file2.txt ...
```

#### Apple Archive

```sh
# ディレクトリの場合
aa archive -d . -subdir dir -o archive.aar
# 複数ファイル/ディレクトリの場合
aa archive -d . -include-path file1.txt -include-path file2.txt -o archive.aar
```
* `-o` で指定したアーカイブ先に既にファイルが存在している場合は新しいアーカイブとして上書きされる (他のアーカイバであればファイルの追加扱い)

#### ar

```sh
ar -r archive.a file1.txt file2.txt
```
* `-r` オプションは新規アーカイブの作成の他に、ファイルの追加、ファイルのアップデートにも使用できる。つまり、既に同名のファイルがアーカイブに含まれていたら削除して新しいものを追加する。
* `-r` オプションと同様の機能を持つのが `-q` オプションである。こちらは `-r` に
比べて高速である代わりに既にファイルが存在しているかは確認せず追加される。
* `-r` と併せて `-v` オプションを使用すると日付が新しい場合のみ置き換えられる

#### Windows Image Format

```sh
wimcapture /path/to/dir image.wim
```

<br>

### アーカイブに追加
```sh
zip -q -r   archive.zip  file3.txt file4.txt ...
tar -r -f   archive.tar  file3.txt file4.txt ...
7zz a -ba   archive.7z   file3.txt file4.txt ...
lha -a -q   archive.lzh  file3.txt file4.txt ...
rar a -inul archive.rar  file3.txt file4.txt ...
zpaq a      archive.zpaq file3.txt file4.txt ...
ar -r       archive.r    file3.txt file4.txt ...

printf "add file3_src.txt file3_dst.txt\nadd file4_src.txt file4_dst.txt" | wimupdate image.wim
```
* tar 以外は実質同じである
* cpio は `-A` オプションの付加でできる仕様だが、未実装らしい
* wim は改行区切りの構成コマンドファイルによりファイルを書き換える

<br>

### ワイルドカードによる指定
```sh
zip -q -r      archive.zip  . -i "file*.txt"
tar -c -f      archive.tar  file*.txt
7zz a -ba -t7z archive.7z   '-ir!file*.txt'
lha -a -q      archive.lzh  file*.txt
rar a -inul    archive.rar  file*.txt
zpaq a         archive.zpaq 'file*.txt'

aa archive -d . -include-regex "file.+\.txt" -o archive.aar

find . -name "file*.txt" | cpio -o -O archive.cpio
```
* tar/lha はシェルの展開を使うしか手法がない
* 7z は `-ir` フラグを使わなくても一応指定できる

<br>

### 指定の除外
```sh
zip -q -r      archive.zip  . -x 'file*.txt'
tar -c -f      archive.tar  --exclude file*.txt
7zz a -ba -t7z archive.7z   '-xr!file*.txt'
lha -a -q      archive.lzh  -x file*.txt
rar a -inul '-xfile*.txt' archive.rar
zpaq a         archive.zpaq -not 'file*.txt'

aa convert -i archive.aar -exclude-file 'file.+\.txt' -o new_archive.aar
```

<br>

### アーカイブを標準出力に送る
```sh
zip -q -r - file.txt
tar -c -f - file.txt

printf file.txt | cpio -o

aa archive -i file.txt
```
* 7z も `-so` フラグで出力できるみたいだが、未実装のようだ
* /dev/stdout への出力もうまくいかない

<br>

### 標準入力をファイルとしてアーカイブ
```sh
printf "some" | 7zz a -t7z archive.7z -sifile.txt
```
* 標準入力の内容がファイル名 `file.txt` としてアーカイブ中に保存される。

<br>

### 空のアーカイブを作成

そのようなファイルが作れて何が嬉しいのかよく分からないが、一応作れてしまう。

* zip

```sh
mkdir _
zip -q -r archive.zip _
zip -q -d archive.zip _
rmdir _
# 22 bytes
# 任意ファイルのアーカイブを作ってそのファイルをアーカイブから削除する
```

* tar

```sh
tar cf archive.tar -T /dev/null
# 1024 bytes
# 空のファイル一覧からアーカイブを作成する
```

* 7-zip

```sh
mkdir _
7zz a -ba -t7z archive.7z _
7zz d -ba archive.7z _
rmdir _
# 32 bytes
# 任意ファイルのアーカイブを作ってそのファイルをアーカイブから削除する
```

* cpio

```sh
printf "" | cpio -o -O archive.cpio
# 87 bytes
# 空のファイル一覧からアーカイブを作成する
```

* lha

```sh
mkdir _
lha -a -q archive.lzh -x=_ _
rmdir _
# 1 byte
# ファイルを指定してアーカイブしようとして、同時にそのファイルを除外指定する
```

* ar

```sh
ar -r archive.a _
# 8 bytes
# 実在しないファイルだけを指定する
```

* Apple Archive

```sh
aa archive -d . -include-path _ -o archive.aar
# 12 bytes
# 実在しないファイルだけを指定する
```

* Windows Image Format

```sh
mkdir _
wimcapture _ image.wim
rmdir _
# 1090 bytes
# 空のフォルダからイメージを作成する
```

* rar は空のアーカイブを作ろうとしてアーカイブの中身を消すとアーカイブ自体が消えてしまうので作れない? やり方があるのなら教えてほしい。

<br>

## 圧縮
### ファイルに保存 (元のファイルは保持)

```sh
gzip -k     file.txt # -> file.txt.gz
bzip2 -z -k file.txt # -> file.txt.bz2
xz -z -k    file.txt # -> file.txt.xz
lzma -z -k  file.txt # -> file.txt.lzma
lz4 -z -q   file.txt # -> file.txt.lz4

lzip -k       -o file.txt.lz       file.txt
lzop          -o file.txt.lzo      file.txt
lrzip -q      -o file.txt.lrz      file.txt
rzip -k       -o file.txt.rz       file.txt
brotli        -o file.txt.br       file.txt
zstd -z -q    -o file.txt.zst      file.txt
lzfse -encode -o file.txt.lzfse -i file.txt
```

* `aa` は保存先を明示的に指定しないと標準出力される
* 圧縮元のファイルがハードリンクの場合、 `gzip`, `bzip2`, `xz`, `lzma` では `-f` オプションを付ける必要がある。

<br>

### ファイルに保存 (元のファイルは削除)

```sh
compress -f    file.txt # -> file.txt.Z
gzip           file.txt # -> file.txt.gz
bzip2 -z       file.txt # -> file.txt.bz2
xz -z          file.txt # -> file.txt.xz
lzma -z        file.txt # -> file.txt.lzma
lz4 -z -q --rm file.txt # -> file.txt.lz4

lzip            -o file.txt.lz   file.txt
lzop -U         -o file.txt.lzo  file.txt
lrzip -q -D     -o file.txt.lrz  file.txt
rzip            -o file.txt.rz   file.txt
brotli --rm     -o file.txt.br   file.txt
zstd -z -q --rm -o file.txt.zst  file.txt
```

* `compress` について
	* 必ず元のファイルが消去されることに注意。
	* `-f` を指定しない場合、圧縮した方がファイルサイズが大きくなるようであれば、圧縮ファイルが生成されない。
	* 実質的に他の圧縮コマンドと同等の機能を実現するには後述のように標準出力に出力して、それを保存した方がよい。

<br>

### 標準出力に書き出し

```sh
compress -f -c   file.txt
gzip -z -k -c    file.txt
bzip2 -z -k -c   file.txt
xz -z -k -c      file.txt
lzma -z -k -c    file.txt
lz4 -z -q -c     file.txt
lzip -k -c       file.txt
lzop -c          file.txt
lrzip -q -o -    file.txt
brotli -c        file.txt
zstd -z -q -c    file.txt
lzfse -encode -i file.txt
```
* `rzip` は標準出力に書き出すことはできない

<br>

## 編集

### ファイル名を変更

```sh
printf "@ old_file.txt\n@=new_file.txt" | zipnote -w archive.zip
7zz rn archive.7z old_file1.txt new_file1.txt old_file2.txt new_file2.txt ...
rar rn -inul old_file1.txt new_file1.txt old_file2.txt new_file2.txt ...
printf "rename old_file.txt new_file.txt" | wimupdate image.wim
```
* `zipnote` は ZIP ファイルのコメントを書き換えるツールであり、これを使うとファイル名までも変更できる

### ファイルを削除

```sh
zip -d -q   archive.zip          file.txt
tar -f      archive.tar --delete file.txt
7zz d -ba   archive.7z           file.txt
lha -d -q   archive.lhz          file.txt
rar d -inul archive.rar          file.txt
ar -d       archive.a            file.txt

printf "delete file.txt" | wimupdate image.wim

# ワイルドカードを使う例
zip -d -q archive.zip "file*.txt"
7zz d -ba archive.7z  "file*.txt"
```
* `tar` は GNU 版の `tar` でのみ有効

### ファイルを新しいものに更新

```sh
zip -f -q   archive.zip  file.txt
tar -u -f   archive.tar  file.txt
7zz u -ba   archive.7z   file.txt
lha -u -q   archive.lhz  file.txt
rar u -inul archive.rar  file.txt
zpaq a -f   archive.zpaq file.txt
ar -r -u    archive.a    file.txt

printf "add file.txt" | wimupdate image.wim
```
* zip で `-f` の代わりに `-u` を用いればファイルがアーカイブ内に存在していない場合に追加する。しかもアーカイブ自体が存在しない場合はアーカイブを作成する。

<br>

## 表示

### アーカイブされたファイル名の一覧を表示

```sh
zipinfo -1 archive.zip
tar tf     archive.tar
rar lb     archive.rar
cpio -t -I archive.cpio
pax -f     archive.pax
aa list -i archive.aar
ar -t      archive.a

wimdir image.wim
```
* BSD版の `tar` コマンドであれば、 tar だけでなく zip, 7z, cpio, pax, lha, rar にも対応し、 tar を gzip などで圧縮したものにも対応する。

<br>

### ファイルの内容を詳細に表示

```sh
zipinfo -l  archive.zip
tar tf      archive.tar -v
7zz l -ba   archive.7z
lha -l -q   archive.lhz # もっと詳しくするには -l の代わりに -v を使用する
rar lt      archive.rar
cpio -tv -I archive.cpio
pax -v -f   archive.pax
zpaq l      archive.zpaq

wimdir --detailed image.wim
```

<br>

## 展開

### 全て展開

```sh
unzip -q      archive.zip # 或いは ditto コマンドを使用 (後述)
tar -x -f     archive.tar
7zz x         archive.7z
lha -x -q     archive.lhz
cpio -i -I    archive.cpio
pax -r -f     archive.pax
rar x -inul   archive.rar
zpaq x        archive.zpaq
ar -x         archive.a
aa extract -i archive.aar
```
* BSD版の `tar` コマンドであれば、 tar だけでなく zip, 7z, cpio, pax, lha, rar にも対応し、 tar を gzip などで圧縮したものにも対応する。

<br>

### 特定のファイル/ディレクトリだけの取り出し展開

```sh
unzip -q    archive.zip  file1.txt file2.txt ...
tar -x -f   archive.tar  file1.txt file2.txt ...
7zz x       archive.7z   file1.txt file2.txt ...
lha -x -q   archive.lhz  file1.txt file2.txt ...
rar x -inul archive.rar  file1.txt file2.txt ...
zpaq x      archive.zpaq file1.txt file2.txt ...
ar -x       archive.a    file1.txt file2.txt ...

aa extract archive.aar -include-path file1.txt -include-path file2.txt ...
```

* ワイルドカードを使う例

```sh
unzip -q archive.zip "file*.txt"
7zz x    archive.7z  "file*.txt"
```

<br>

### 標準出力に展開

```sh
unzip -q -p  archive.zip file.txt
tar -x -f -O archive.tar file.txt
lha -p -q    archive.lhz file.txt
ar -p        archive.a   file.txt

aar extract -i archive.aar
```
* 7z ではサポートされていない

<br>

### 特定のディレクトリに展開

```sh
unzip -q    archive.zip   -d output_dir
ditto -x -k archive.zip      output_dir

tar -x -f   archive.tar   -C output_dir
7zz x       archive.7z     -ooutput_dir
lha -x -q   archive.lhz   -w output_dir
rar x -inul archive.rar      output_dir
zpaq x      archive.zpaq -to output_dir

aa extract -i archive.aar -d output_dir

wimapply image.wim output_dir
```
* 日本語を含むファイル名のファイルは `unzip` ではなく `ditto` を使用しないと展開できないことがある。
* `zpaq` の `-to` オプションはアーカイブ時/展開時にファイル名を変更する役割がある。
* `rar` では展開先ディレクトリと展開ファイルの指定がどちらもフラグなしで指定できる。指定の順番は特になく、混ぜてもよい。

<br>

## 解凍

### ファイルに保存

```sh
uncompress -f    file.txt.Z # 元の圧縮ファイルは残らない
gzip -d -k       file.txt.gz
bzip2 -d -k      file.txt.bz2
xz -d -k         file.txt.xz
lzma -d -k       file.txt.lzma
lz4 -d -q        file.txt.lz4
lzip -d -k       file.txt.lz
lzop -d          file.txt.lzo
lrzip -d -q      file.txt.lrz

rzip -d       -o file.txt    file.txt.rz   
brotli -d -q  -o file.txt    file.txt.br   
zstd -d -q    -o file.txt    file.txt.zst  
lzfse -decode -o file.txt -i file.txt.lzfse
```

<br>

### 標準出力に書き出し

```sh
uncompress -f -c file.txt.Z
gzip -d -c       file.txt.gz
bzip2 -d -c      file.txt.bz2
xz -d -c         file.txt.xz
lzma -d -c       file.txt.lzma
lzip -d -c       file.txt.lz
lzop -d -c       file.txt.lzo
lz4 -d -q -c     file.txt.lz4
lrzip -d -q -o - file.txt.lrz
brotli -d -q -c  file.txt.br
zstd -d -q -c    file.txt.zst
lzfse -decode -i file.txt.lzfse
```

<br>

## オプション

### 対応フォーマット/アルゴリズム

| 引数指定 | デフォルト値 | 指定可能な値 |
|:--|:--:|:--|
| `zip -Z [method]` | `deflate` | `store`,`deflate`,`bzip2` |
| `tar --format [fmt]` | `ustar` | `cpio`,`pax`,`shar`,`ustar` |
| `7zz -t[fmt]` | `7z` | `7z`,`zip`,`gzip`,`bzip2`,`lzip`,`tar` |
| `xz -F [fmt]` | `xz` | `xz`,`lzma` |
| `lzma -F [fmt]` | `lzma` | `xz`,`lzma` |
| `lrzip [fmt]` | なし | なし,`--bzip2`,`--gzip`,`--lzo`,`--zpaq`,`--no-compress` |
| `zstd --format=[fmt]` | `zstd` | `zstd`,`gzip`,`xz`,`lzma`,`lz4` |
| `aa -a [algorithm]` | `lzfse` | `lzfse`,`lzma`,`lz4`,`zlib`,`raw` |

### 圧縮率

| 引数指定 | デフォルト値 | 値の範囲 |
|:--|:--:|:--:|
| `zip -[level]` | 6 | 1-9 |
| `7zz -mx=[level]` | 5 | 0,1,3,5,7,9 |
| `rar -m[level]` | 3 | 0-5 |
| `gzip -[level]` | 6 | 1-9 |
| `bzip2 -[level]` | 9 | 1-9 |
| `xz -[level]` | 6 | 0-9 |
| `lzma -[level]` | 6 | 0-9 |
| `lz4 -[level]` | 1 | 1-12 |
| `lzip -[level]` | 6 | 0-9 |
| `lzop -[level]` | 3 | 1-9 |
| `lrzip -L [level]` | 7 | 1-9 |
| `rzip -[level]` | 6 | 0-9 |
| `brotli -q [level]` | 11 | 0-11 |
| `zstd -[level]` | 3 | 1-19 |

### tar + 圧縮 の組み合わせ

* フラグは `tar` コマンドのオプションとして指定するフラグ。 tar アーカイブとまとめて圧縮も実行してくれる。
* BSD版tar と GNU版tar でのフラグの対応状況も記す。
* それぞれ対応した圧縮•解凍コマンドを用意していないと動作しない。

| | 拡張子 | フラグ | BSD tar | GNU tar |
|:--|:--|:--|:--:|:--:|
| tar + compress | `.tar.Z` | `--compress`,`-Z` | ○ | ○ |
| tar + gzip | `.tgz`, `.tar.gz` | `--gzip`,`-z` | ○ | ○ |
| tar + bzip2 | `.tbz2`, `.tar.bz2` | `--bzip2`, `-j` | ○ | ○ |
| tar + xz | `.txz`, `.tar.xz` | `--xz`, `-J` | ○ | ○ |
| tar + lzma | `.tar.lzma` | `--lzma` | ○ | ○ |
| tar + lzop | `.tzo`, `.tar.lzo` | `--lzop` | ○ | ○ |
| tar + zstd | `.tzst`, `.tar.zst` | `--zstd` | ○ | ○ |
| tar + lzip | `.tlz`, `.tar.lz` | `--lzip` | × | ○ |
| tar + lz4 | `.tar.lz4` | `--lz4` | ○ | × |
| tar + lrzip | `.tlrz`, `.tar.lrz` | `--lrzip` | ○ | × |
| tar + brotli | `.tbr`, `.tar.br` | - | × | × |
| tar + rzip | `.tar.rz` | - | × | × |