行番号の桁数を変更するnlコマンドのwオプション


nlコマンドはwオプションを指定することで、行番号の桁数を変更することができます。

# wオプションの使い方

まずはマニュアルを参照してみましょう。

## info nl (GNU coreutils)

```console
‘-w NUMBER’
‘--number-width=NUMBER’
     Use NUMBER characters for line numbers (default 6).
```

coreutilsは非常にシンプルに記載されています。桁数を変更するには`-w`のあとに数字を指定するだけです。そして[-nオプションの記事](https://qiita.com/Carol_815/items/f3ebf5005a94f58f6055)で調べたとおり、デフォルトの桁数は6桁になります。

## busybox nl --help

```console
	-w N		Width of line numbers
```

busyboxでも-wオプションは使うことができます。ヘルプはcoreutils以上にシンプルな記述です。

## man nl (BSD)
```console
     -w width     Specify the number of characters to be occupied by the line num-
                  ber; in case the width is insufficient to hold the line number,
                  it will be truncated to its width least significant digits.  The
                  default width is 6.
```

一方でBSDのマニュアルにはcoreutilsに比べて長い説明が書かれています。主な差分は「幅が行番号を保持するのに十分でない場合、行番号は有効な幅の桁数以下に切り捨てられる」ということのようです。これはあとで検証してみましょう。

## POSIX

> -w  width
Specify the number of characters to be used for the line number. The default width shall be 6.

出典：https://pubs.opengroup.org/onlinepubs/9699919799/utilities/nl.html

ちなみにPOSIXでは「幅が十分でない場合」について言及は無いようです。この条件において互換性は保証されていません。


# wオプションを試す

それでは[最初の記事](https://qiita.com/Carol_815/items/d1081ff7a7d3f9971edc)で使用したテキストファイル「utl-kita」で実際に試してみましょう。

```console
$ nl -w 1 utl-kita
1	東京
2	上野
3	尾久
4	赤羽
5	浦和
6	さいたま新都心
7	大宮
$ nl -w 10 utl-kita
         1	東京
         2	上野
         3	尾久
         4	赤羽
         5	浦和
         6	さいたま新都心
         7	大宮
```

たしかに幅が変更されてはいますが、少々わかりにくいです。これを`-n`でゼロ埋めして出力すればより明瞭になります。

```console
$ nl -n rz -w 2 utl-kita
01	東京
02	上野
03	尾久
04	赤羽
05	浦和
06	さいたま新都心
07	大宮
```

## 駅ナンバリング表示の駅番号を出力する

2桁の数字と駅名を表示したら、あの見慣れた表示も出力したくなりますよね。宇都宮線・高崎線の略線記号「JU」を付加すれば、駅ナンバリング表示の駅番号を出力することができます。プリフィックスの付加には、`sed`（Stream EDitor）というテキストを編集するコマンドを組み合わせます。

```console
$ nl -n rz -w 2 utl-kita | sed 's/^/JU/'
JU01	東京
JU02	上野
JU03	尾久
JU04	赤羽
JU05	浦和
JU06	さいたま新都心
JU07	大宮
```
`sed`にも軽く触れておくと、`s/^/JU/`の部分は、テキストを置換する`s`コマンドで、正規表現`^`（行頭）にマッチする部分を`JU`に置き換えるという意味です。`/`はただの区切り文字なので、`sed`の文法上は`s@^@JU@`などとしても同じ意味になります。


# 幅が十分でない場合

さきほどBSDのマニュアルで差分があった部分です。しっかり検証しておきましょう。6桁から7桁になるとき、つまり`999999`から`1000000`になる部分を出力して試してみます。

まずはcoreutilsから。

```console:coreutils
$ yes | head -n 1000000 | nl | tail -n 2
999999	y
1000000	y
```
7桁の数字もきちんと出力されています。もちろん`-w`でデフォルト以外の桁数を指定しても同様の挙動になります。

```console:coreutils
$ yes | head -n 100 | nl -w 2 | tail -n 2
99	y
100	y
```
busyboxでも試してみましょう。

```console:busybox
$ yes | head -n 100 | busybox nl -w 2 | tail -n 2
99	y
100	y
```

他のコマンドにも触れておくと、`yes`の出力「y」を`head`で1000000行あるいは100行を切り取り、`nl`で行番号を付加してから`tail`で最後の2行だけを出力しています。

続いてBSD系の`nl`ではどうでしょうか。macOSの`nl`で試してみます。

```console:BSD
$ yes | head -n 1000000 | nl | tail -n 2
999999	y
000000	y
```

どうやら本当に6桁を超えると上位の桁が切り捨てられるようです。つまり**BSD系の`nl`では`-w`の設定なしに100万行を超える行番号を正しくカウントすることはできません。**念の為に内部的に行番号が正しく保持されているかどうか確認してみましょう。同じ条件で幅を7桁にしてみます。

```console:BSD
$ yes | head -n 1000000 | nl -w 7 | tail -n 2
 999999	y
1000000	y
```
この場合はきちんと7桁の数字が出力されました。行番号自体は正しく保持されているようです。


以上の検証結果をまとめると、coreutilsとbusyboxの`nl`では`-w`は最小の桁数を指定しており、桁が大きくなった場合その幅は可変です。一方で、BSD系の`nl`では`-w`は固定の桁数を指定しており、その幅を越えた場合は上位の桁が切り捨てられます。このように`nl`は実装によって桁の取り扱いが異なり、`-w`の設定値を意識しなければならない場面もありそうです。

---