0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

nlコマンドAdvent Calendar 2020

Day 15

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

Last updated at Posted at 2020-12-14

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

1論理ページの解釈

マニュアルの記述を参照すると、行番号は「それぞれの論理ページごとにリセット」されるとあります。実際の記述では、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.」と書かれています。
しかし「論理ページの開始」は曖昧であり、論理ページを使いこなすには実際の挙動を知っておくべきだと思います。様々なセクションを含むデータの場合、各実装で行番号のリセットがどのようなタイミングで実施されるか確認してみましょう。

リセットのタイミング

入力例
$ 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をつけて、行番号を振るようにして検証してみます。

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
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がヘッダーの開始ではなく論理ページをを認識して行番号がリセットされていることが確認できます。

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のほうが使いやすい挙動に思えるでしょう。

いずれにせよ、あまり使われていない機能であることはだいぶ長い期間バグが放置されていた品質面からみても事実のようで、どう使うべきか明確でないまま標準化されてしまっているようにも思います。つまり実質的に使われていない機能ですから、どっちでもいいんじゃないかってことです。(ひどい)

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

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

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
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でこの機能差異が生じた際、私には意見できる機会がありました。バグ修正のときのことです。先に書いたとおり結論から言えばどっちでもいいと思うのですが、私の理解不足と思慮不足によりメンテナーの方たちと十分な議論を重ねることはできませんでした。
当時、行番号をリセットするパッチを書いて送ったところ、メンテナーの方がヘッダーとフッターのリセット処理を加えた上でレビューを求めてきました。しかし私は名古屋に旅行中でall day 飲酒だったので「まあこれでいいんじゃん」みたいな適当な返事をしてしまいました。その時の私のパッチのままではBSDの実装と同等にはなりませんが、しっかりと議論できず結果的に機能差異ともやもやを残すことになってしまったのは残念です。心残りはありますが、お酒は美味しかったので仕方ないと思います。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?