nlコマンドの最大値


nlコマンドの細かい仕様を調べていくと最大値や最小値も気になりますね。実装ごとの仕様やエラーについてまとめておこうと思います。

# 行番号

つまり何行まで数えられるかということです。[BSD系のnlではデフォルトの桁数がボトルネックになる](https://qiita.com/Carol_815/items/fa16e4875ba7bc19de86#%E5%B9%85%E3%81%8C%E5%8D%81%E5%88%86%E3%81%A7%E3%81%AA%E3%81%84%E5%A0%B4%E5%90%88)ことは以前も確認しましたが、今回は「行数」の最大値を見ていきます。また、x86_64(AMD64)のアーキテクチャで検証していますので、環境によっては異なる可能性もあると思います。

## GNU coreutils

```console:coreutils~v8.32
$ yes unko | nl -v 9223372036854775806
9223372036854775806	unko
9223372036854775807	nl: line number overflow
```

coreutilsの`nl`では符号付き64bitのようです。`9,223,372,036,854,775,807`が最大値と言いたいところですが、厳密には`unko`を出し切らずにオーバーフローでエラーになっています。しかしこの中途半端なエラーも[2020年10月の修正](https://qiita.com/Carol_815/items/9e912946d4db1f22ce20#coreutils%E3%81%AEnl%E3%81%AE%E6%96%B0%E6%A9%9F%E8%83%BD)のついでに改善されているので、次版（v8.33？）以降では最後まで出し切ってからエラーになります。

```console:coreutils-next
$ yes unko | nl -v 9223372036854775806
 9223372036854775806	unko
 9223372036854775807	unko
 nl: line number overflow
```

どの行でオーバーフローしたのかが明確になるので、少々デバッグが楽になるんじゃないかと思います。900京行超えのテキストファイルなんて絶対触りたくないですけどね。


## BSD系

```console:BSD(macOS)
$ yes unko | nl -v 2147483647 -w 11 | head -2
 2147483647	unko
-2147483648	unko
```

BSD系の`nl`では符号付き32bitのようです。つまり`2,147,483,647`が最大です。
そしてオーバーフローしません。そのまま符号がひっくり返ってカウントを続けます。つまり正しい行番号を出力しませんが、エラーが発生して終了することもありません。


## busybox

busyboxはちょっとおもしろい挙動です。また、負の値には対応していません。

```console:busybox
$ yes unko | busybox nl -v 2147483648 | head
nl: number 2147483648 is not in 0..2147483647 range
```

オプションに大きな数を指定するとエラーになります。符号付き32bitの正の値だけが有効のようです。しかし、実際に使ってみると上限はほかのところにあります。

```console
$ yes unko | busybox nl -v 2147483647 | head -n 2147483647 | tail
4294967284	unko
4294967285	unko
4294967286	unko
4294967287	unko
4294967288	unko
4294967289	unko
4294967290	unko
4294967291	unko
4294967292	unko
4294967293	unko
```

あまり厳密ではありませんが（回しっぱなしにしてみたら超時間かかったのでやり直したくない）、符号なし32bitまではカウントできるようです。

### 変数の型を確認する
実行が中途半端になってしまったので、代わりにbusyboxのソースを確認しましょう。
まずは、-vオプションの引数処理です。
https://github.com/mirror/busybox/blob/master/coreutils/nl.c

```c

	getopt32long(argv, "pw:+s:v:+i:+b:", nl_longopts,
			&ns.width, &ns.sep, &ns.start, &ns.inc, &opt_b);
```

`pw:+s:v:+i:+b:`の`v:+`という部分が、`-v`の識別子ですね。
https://github.com/mirror/busybox/blob/master/libbb/getopt32.c

```c

 "o:+"  This means that the parameter for this option is a nonnegative integer.
        It will be processed with xatoi_positive() - allowed range
        is 0..INT_MAX.
```

こちらを確認すると、`:+`は`0..INT_MAX`ということのようです。

あとは行番号の処理も見ましょう。ちなみにbusyboxではこの処理は`cat -n`と共通です。
https://github.com/mirror/busybox/blob/master/libbb/print_numbered_lines.c

```c
void FAST_FUNC print_numbered_lines(struct number_state *ns, const char *filename)
{
	FILE *fp = fopen_or_warn_stdin(filename);
	unsigned N = ns->start;
```
`unsigned N`がカウンタの変数です。つまり、オプションを取得する関数内で正の数に制限されているだけで、カウンタ自体は2倍の`unsigned`までカウントできるようです。


#エラー

もしシェルスクリプトなどでエラー処理を書く場合は、戻り値（終了ステータス）を見ることになります。`bash`では戻り値が変数`$?`に格納されるので、それを確認することもできます。

## POSIX

>EXIT STATUS
The following exit values shall be returned:
> 0
Successful completion.
>\>0
An error occurred.

POSIXでは成功した場合は`0`、エラーは`0より大きい`と決められています。各実装でエラーのときどんな値が返ってくるのか見てみましょう。存在しないオプションを実行してみます。

## GNU coreutils

```console:coreutils
$ nl -a &>/dev/null || echo $?
1
```

## busybox

```console:busybox
$ busybox nl -a &>/dev/null || echo $?
1
```
## BSD系

```console:BSD(macOS)
$ nl -a &>/dev/null || echo $?
1
```

どれも`1`が返ってきました。

---