nlコマンドの通し番号制御

論理ページと通し番号の制御方法について一通り説明したところで、もう少々細かい話をしたいと思います。以下の記事の内容を前提にしています。

- [nlコマンドの論理ページ](https://qiita.com/Carol_815/items/d1f52df36b0d39b374b0)
- [行番号の付番方式を変更するnlコマンドのb,f,hオプション](https://qiita.com/Carol_815/items/997de8239ce3402b46dc)
- [論理ページごとに行番号をリセットしないnlコマンドのpオプション](https://qiita.com/Carol_815/items/ac3efeb363c6edb32bba)

# 1論理ページの解釈

[マニュアルの記述を参照](https://qiita.com/Carol_815/items/d1f52df36b0d39b374b0#%E8%AB%96%E7%90%86%E3%83%9A%E3%83%BC%E3%82%B8%E3%81%AE%E4%BD%BF%E3%81%84%E6%96%B9)すると、行番号は「それぞれの論理ページごとにリセット」されるとあります。実際の記述では、coreutilsは「by default, the line number is reset to 1 at each logical page section.」、POSIXは「Line numbering shall be reset at the start of each logical page.」と書かれています。
しかし「論理ページの開始」は曖昧であり、論理ページを使いこなすには実際の挙動を知っておくべきだと思います。様々なセクションを含むデータの場合、各実装で行番号のリセットがどのようなタイミングで実施されるか確認してみましょう。

## リセットのタイミング

```console:入力例
$ echo -e '\:\:\:\na\n\:\:\nb\n\:\nc\n\:\:\:\nd\n\:\:\ne\n\:\nf'
\:\:\:
a
\:\:
b
\:
c
\:\:\:
d
\:\:
e
\:
f
```

このサンプルデータはいわゆる「2ページ物」の例です。1ページは「ヘッダー」「ボディ」「フッター」の3つのセクションから構成されています。`nl`はデフォルトではヘッダーとフッターに行番号を振りませんので、`-f t -h t`をつけて、行番号を振るようにして検証してみます。

```console:coreutils
$ echo -e '\:\:\:\na\n\:\:\nb\n\:\nc\n\:\:\:\nd\n\:\:\ne\n\:\nf' | nl -f t -h t

     1	a

     1	b

     1	c

     1	d

     1	e

     1	f
```

```console:BSD
$ echo -e '\:\:\:\na\n\:\:\nb\n\:\nc\n\:\:\:\nd\n\:\:\ne\n\:\nf' | nl -f t -h t
     1	a
     2	b
     3	c
     1	d
     2	e
     3	f
```

出力されたデータをみると、coreutilsの`nl`ではすべてのセクション開始ごとにリセットされていますが、BSD系の`nl`ではセクションによらず論理ページのヘッダー開始ごとにリセットされていることがわかります。
少々意地悪ですが、ページの間にボディだけのページを挿入したデータを試してみれば、BSD系の`nl`がヘッダーの開始ではなく論理ページをを認識して行番号がリセットされていることが確認できます。

```console:BSD
$ echo -e '\:\:\:\na\n\:\:\nb\n\:\nc\n\:\:\nX\n\:\:\:\nd\n\:\:\ne\n\:\nf' 
\:\:\:
a
\:\:
b
\:
c
\:\:
X
\:\:\:
d
\:\:
e
\:
f
$ echo -e '\:\:\:\na\n\:\:\nb\n\:\nc\n\:\:\nX\n\:\:\:\nd\n\:\:\ne\n\:\nf' | nl -f t -h t
     1	a
     2	b
     3	c
     1	X
     1	d
     2	e
     3	f
```

この挙動の違いですが、POSIXにも細かな記載は無くどちらが正しいということはおそらくありません。しかし用語的な観点でみれば、「論理ページ」の構成要素が「ヘッダー」「ボディ」「フッター」とみなすほうが自然に思えます。その場合、セクションの区切りは改ページではないので、BSD系の`nl`のように通し番号とする実装が採用されるべきと思います。一方で、本文である「ボディ」とは異なる構成要素である「ヘッダー」や「フッター」が通し番号であって良いのかという疑問も生じます。この点に注目すればcoreutilsの`nl`のほうが使いやすい挙動に思えるでしょう。

いずれにせよ、あまり使われていない機能であることは[だいぶ長い期間バグが放置されていた](https://qiita.com/Carol_815/items/ac3efeb363c6edb32bba#%E5%8F%A4%E3%81%84coreutils%E3%81%AEnlv825%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6)品質面からみても事実のようで、どう使うべきか明確でないまま標準化されてしまっているようにも思います。つまり実質的に使われていない機能ですから、どっちでもいいんじゃないかってことです。（ひどい）

## リセットしない場合の挙動

`-p`を使用してリセットしないようにした場合の出力はほぼ同等で、通し番号になります。

```console:coreutils
$ echo -e '\:\:\:\na\n\:\:\nb\n\:\nc\n\:\:\:\nd\n\:\:\ne\n\:\nf' | nl -f t -h t -p

     1	a

     2	b

     3	c

     4	d

     5	e

     6	f
```

```console:BSD
$ echo -e '\:\:\:\na\n\:\:\nb\n\:\nc\n\:\:\:\nd\n\:\:\ne\n\:\nf' | nl -f t -h t -p
     1	a
     2	b
     3	c
     4	d
     5	e
     6	f
```

---

# 余談

実はcoreutilsでこの機能差異が生じた際、私には意見できる機会がありました。[バグ修正](https://qiita.com/Carol_815/items/ac3efeb363c6edb32bba#%E5%8F%A4%E3%81%84coreutils%E3%81%AEnlv825%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6)のときのことです。先に書いたとおり結論から言えばどっちでもいいと思うのですが、私の理解不足と思慮不足によりメンテナーの方たちと十分な議論を重ねることはできませんでした。
当時、行番号をリセットするパッチを書いて送ったところ、メンテナーの方がヘッダーとフッターのリセット処理を加えた上でレビューを求めてきました。しかし私は名古屋に旅行中でall day 飲酒だったので「まあこれでいいんじゃん」みたいな適当な返事をしてしまいました。その時の私のパッチのままではBSDの実装と同等にはなりませんが、しっかりと議論できず結果的に機能差異ともやもやを残すことになってしまったのは残念です。心残りはありますが、お酒は美味しかったので仕方ないと思います。