次期 POSIX (Issue 8) で head -c が標準化されます！ 〜 今まではファイルの先頭バイトを取得するのに dd を使えと言っていた

## はじめに

すみません。嘘つきました。`dd` コマンドを使えとは言ってないです。POSIX は「`head` に `-c` オプションなんてないよ。**別のコマンド**が使えるよ」って言ってます。別のコマンドってなんやねん！

https://pubs.opengroup.org/onlinepubs/9699919799/utilities/head.html

> There is no -c option (as there is in tail) because it is not historical practice and because other utilities in this volume of POSIX.1-2017 provide similar functionality.

まあ、誰も `dd` コマンドしか思い浮かばないので間違いないでしょう。

実は `tail` コマンドには `-c` オプションがあります（しかもやけに高機能です）。しかし `head` には `-c` オプションは規定されていませんでした。なんだこの非対称性は？と思いますがそれが POSIX です。最低限のものしか標準化しませんし `-c` オプションが使えない実装があるならなおのこと標準化しません。・・・が、**それが来年（2022 年後期）変わります！**

## POSIX (Issue 8) で `head -c` が標準化

次期 POSIX (Issue 8) で `head -c` が標準化されることになりました。これで安心（？）して使えますね。（後述しますが、大抵の環境はすでに使えるはずですが macOS を除く商用 Unix は最新の POSIX / UNIX 標準規格への対応が鈍いのでそれらに対応する必要がある場合は注意が必要です）

https://www.austingroupbugs.net/view.php?id=407

新しいドキュメントは以下のように `dd` コマンドを使った例、そしてそれがパフォーマンスが悪いということが明記されます。

> Older implementations of head did not support -c number, but emulating
> this via `dd ibs=1 count=number` is much less efficient and emulating
> via `dd obs=pipe_buf | dd ibs=size count=number_of_blocks` is cumbersome,
> somewhat less efficient, and can only be used if the number of bytes
> to be copied is a multiple of a suitable block size less than or equal
> to PIPE_BUF.

## POSIX で標準化されてなかったのは移植性がなかったから

基本的に POSIX というのは新しく仕様を作る団体ではなく、既存の実装で移植性があるものを標準化しているだけなので POSIX で標準化されていないというのは、昔の Unix 系 OS では `-c` オプションはどこでも使えるオプションでなかったのだろう推測できます。そのことは POSIX にも書かれてあるとおりです。

> because it is not historical practice 

ということで、少し調べてみました。どうも `head` 自体が `tail` よりもあとに作られているようです。[List of Unix commands](https://en.wikipedia.org/wiki/List_of_Unix_commands) を見ると `head` は PWB UNIX (1977) で登場したと書いてあるのですが要出典となっていて怪しいので、さらに調べると [PWB/UNIX 1.0](https://www.tuhs.org/cgi-bin/utree.pl?file=PWB1/sys/source/s2/) にはないように見えます。そして[1BSD](https://www.tuhs.org/cgi-bin/utree.pl?file=1BSD/s6/head.c) (1978) にはありました。[Unix V8](https://www.tuhs.org/cgi-bin/utree.pl?file=V8/usr/src/cmd/head.c) (1985) にはありますが `Bill Joy UCB August 24, 1977` と書いてますし、1BSD で作られたように思えます。`tail` コマンドの方は [PWB/UNIX 1.0 の tail](https://www.tuhs.org/cgi-bin/utree.pl?file=PWB1/sys/source/s2/tail.c)が最初のように思えます（この時点から書式は違いますが `-c` 相当の機能があります）。

ここからの私の推測は `tail` (1977, PWB/UNIX) → `head` (1978, 1BSD) という順番で登場し、`head` は `tail` を参考にしているものの異なる開発者によって作られたコマンドであるということです。開発者が異なれば機能に違いがあっても不思議ではありません。Unix Version 10 (1989) の時点でも [Unix V10 head](https://www.tuhs.org/cgi-bin/utree.pl?file=V10/cmd/head.c) と [Unix V10 tail](https://www.tuhs.org/cgi-bin/utree.pl?file=V10/cmd/tail.c) でオプションが違うので、POSIX の RATIONALE で説明してあるとおり `head -c` は昔は移植性が低かったから POSIX で標準化されなかったということなのでしょう。

## `head -<N>` や `tail -<N>` は POSIX 準拠ではない

`head` コマンドも `tail` コマンドも POSIX.2-1988 で 最初から採用されています（参考 [POSIX + SUS 全コマンド一覧（全バージョン対応）](https://qiita.com/ko1nksm/items/ad88974b48e91d0289a2)）。一方 XPG の方では POSIX に準拠する形で Issue 4 から `head` コマンドが採用されています。`tail` コマンドは Issue 2 （最初）から採用されているようです。

ソースコードをざっと読んだ限りですが、POSIX 標準化前と後では書式が異なるようです。歴史的な書式は次のようなものです。

```sh
head -10 file # 最初から 10 行表示
tail -10 file # 最後から 10 行表示
tail -10c file # 最後から 10 バイト表示
```

**この書式は POSIX 準拠ではありません。POSIX に準拠するのであれば `-n` または `-c` オプションを使用する必要があります。**[Issue 6 で削除](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/head.html#tag_20_57_22)という記述がありますが、おそらく Issue 5 以前の XPG 側の仕様の話で POSIX の話ではありません。POSIX では POSIX.2-1992 で標準化した時に [Utility Syntax Guidelines](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html#tag_12_02) に従って `-n` オプションや `-c` オプションを標準化したようです（未確認）。

## 商用 UNIX は `head -c` の使用に注意

主要な実装で `head -c` は使えると言っても、macOS を除く商用 UNIX は例外です。ところで商用 UNIX ってこれから、最新の POSIX に準拠していく気はあるんでしょうかね？現状維持のままサポートだけを続けるのでしょうか？

`head -c` が使えない場合は関数でラップしたりして互換性問題を吸収する必要があります（参考 [実践！ポータブルシェルスクリプト 〜 シェル関数によるLinuxとmacOSの移植性・互換性問題の攻略方法](https://qiita.com/ko1nksm/items/4f2c4bdf320b78751bc1)）。また、商用 UNIX はデフォルトで POSIX 準拠モードになってない可能性が高いので注意してください（参考 [「え？UNIXなのにPOSIXに準拠してないの！？」シェルスクリプト実行環境をPOSIX準拠モードに変更して互換性を上げる方法 (Solaris, AIX, HP-UX, Linux, macOS, BSD)](https://qiita.com/ko1nksm/items/6ebdcba7050da2f3c585)）。

### Solaris 

Solaris 10 /11 は互換性維持のためにデフォルトが歴史的なコマンドになっているので `head` コマンドも歴史的な古いコマンドだろうと予測できるので確認してみました。以下は Solaris 10 での実行結果ですが、Solaris 11 でも同じでした。また `/usr/xpg6/bin` には `head` / `tail` コマンドはありません。（以下はつい `--help` を使ってますが、こんなオプションはありません。実際にはエラーメッセージです。）

```sh
$ /usr/bin/head --help # 歴史的版だが POSIX にも準拠している
/usr/bin/head: illegal option -- help
usage: head [-n #] [-#] [filename...]

$ /usr/xpg4/bin/head --help # こちらにはない
bash: /usr/xpg4/bin/head: No such file or directory
```

```sh
$ /usr/bin/tail --help # 歴史的版（POSIX 非準拠）
usage: tail [+/-[n][lbc][f]] [file]
       tail [+/-[n][l][r|f]] [file]

$ /usr/xpg4/bin/tail --help # XPG4版（POSIX 準拠）
usage: tail [-f|-r] [-c number | -n number] [file]
       tail [+/-[number][lbc][f]] [file]
       tail [+/-[number][l][r|f]] [file]
```

ということで Solaris では `head -c` は使えませんし、`tail` も「POSIX準拠モード」にしなければ POSIX に準拠しません。

### AIX

AIX 7.2 では `head -c` も使えるように見えます。

- [head コマンド](https://www.ibm.com/docs/ja/aix/7.2?topic=h-head-command)
- [tail コマンド](https://www.ibm.com/docs/ja/aix/7.2?topic=t-tail-command)

ただし AIX は「行は改行で終わるもの」という文化があるのでバイト指定でも改行を追加しそうです。もしそうなら改行を消すワークアラウンドを追加するか `dd` コマンドを使って実装する必要があるでしょう。

### HP-UX

HP-UX では `head -c -n 10` のような指定の仕方をするようです。残念ながら互換性がないです。こちらもワークアラウンドが必要です。

ドキュメントは、[ここ](https://support.hpe.com/connect/s/?language=ja) から「HP-UX リファレンス ユーザーコマンド v3」で検索（重すぎ）

- [HP-UX Reference (11i v3 07/02) - 1 User Commands A-M (vol 1) (HP-UX リファレンス (11i v3 07/02) セクション 1 : ユーザーコマンド (A～M) (Vol. 1))](https://support.hpe.com/hpesc/public/api/document/c01976051) [PDF]
- [HP-UX Reference (11i v3 07/02) - 1 User Commands N-Z (vol 2) (HP-UX リファレンス (11i v3 07/02) セクション 1 : ユーザーコマンド (N～Z) (Vol. 2))](https://support.hpe.com/hpesc/public/api/document/c01976092) [PDF]

head

> 構文
>   head [-c | -l] [-n count ] [ file ... ]
> 旧版
>   head [-count ] [ file ... ]

tail

> 構文
>   tail [-f] [-b number ] [ file ]
>   tail [-f] [-c number ] [ file ]
>   tail [-f] [-n number ] [ file ]
> 旧版
>   tail [±[ number ] [l b c] [-f] [ file ]

## さいごに

ということで、これからは POSIX 準拠のシェルスクリプトでも `head -c` は安心して使えるようになりますよという話でした。まあ実際には（まだ）POSIX Issue 8 の仕様に対応してない環境もあるわけで、どこまで対応するかっていうのはケース・バイ・ケースで判断することになるでしょう。ですが少なくともこれからは、`head -c` を使っても移植性があると言えますし、動かない環境があれば「POSIX に準拠してない OS が悪い」と OS のせいにすることができます（笑）

一部の人にとっての悩みは商用 UNIX でしょう。商用 UNIX は最新の UNIX V7 (SUSv4 = Issue 7) の認証すら受けてないものが多いですし、これから POSIX (Issue 8) に準拠していくのか気になります。このまま 商用 UNIX が実装しないせいで POSIX が停滞してしまうのも困りますし、少しずつでも使いやすく進化していくと嬉しいですね。