様々なnlコマンドの実装


一口に「nlコマンド」と言っても様々な実装があります。機能に大きな差はありませんが、この記事では`nl`の種類とその調べ方について説明していきます。

# nlコマンドの種類

## 最初のnlコマンド

最初に`nl`が実装されたシステムは、1984年に発売されたAT&Tの「System V Release 2（SVR2）」のようです。FreeBSDのドキュメントにはこのような記載がありました。

> HISTORY
>      The nl utility first appeared in AT&T System V Release 2 UNIX.

出典：https://www.freebsd.org/cgi/man.cgi?query=nl

さらには1987年、X/Open Portability Guide（issue 2）で標準化され、POSIXやSingle Unix Specificationといった後の主要な標準ドキュメントにも記載されていきます。30年以上の歴史あるコマンドですから、今回紹介する実装以外にも様々な`nl`が存在することでしょう。

## GNU coreutils

GNU coreutilsは多くのLinuxディストリビューションで採用されている`nl`を含む主要なコマンドユーティリティです。他の実装に比べて、GNUによる独自の機能拡張や性能改善がされています。この拡張が互換性や移植性の問題を引き起こすこともありますが、個人的な利用において過度に気にする必要は無いでしょう。

執筆時点の最新版はv8.32で2020年3月にリリースされたものです。最近のLinuxディストリビューションでは、RHEL8やCentOS8、Ubuntu 20.04LTSではv8.30が、Ubuntu 20.10ではv8.32が採用されているようです。

## BSD系（macOS、FreeBSD等）

BSD系の`nl`は単一の提供元が存在するわけではなく、*BSDの実装を元にAppleなどの各OSベンダーが必要に応じた改変を加え提供している`nl`の総称的なものになります。もちろん細かな挙動は各々の実装で異なる可能性がありますし、プロプライエタリな実装もあります。
ちなみに[AppleのSource Browser](https://opensource.apple.com/source/text_cmds/text_cmds-101.40.1/nl/nl.c.auto.html)によると、macOSではNetBSDの実装が使われているようです。

# busybox

ルーターなどのLinux組み込み機器では、コマンドユーティリティにbusyboxが採用されていることがあります。busyboxは単一の実行ファイルで、呼び出されたときのコマンド名に応じて様々なコマンドの動作が可能な特殊なソフトウェアです。もちろん`nl`の機能も内包されています。実行ファイルのサイズがとても小さいので、組み込み機器など記憶領域に制約がある環境で採用されています。


# 種類やバージョンの調べ方

## --versionオプション
`--version`オプションでコマンドのバージョンを見ることができます。このとき著作権に関する情報も出力されるので、どんな`nl`なのかを知ることができます。しかしcoreutils以外の`nl`の実装では、`--version`オプションに対応しておらずエラーと簡単な使い方だけが表示されてバージョンが判明しないこともあります。

```console:Ubuntu&#x20;20.04&#x20;LTS
$ nl --version
nl (GNU coreutils) 8.30
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Written by Scott Bartram and David MacKenzie.
```

Ubuntuの`nl`では、`--version`オプションを使うことで、GNU coreutilsであることがわかりました。

```console:macOS&#x20;Catalina
% nl --version
nl: illegal option -- -
usage: nl [-p] [-b type] [-d delim] [-f type] [-h type] [-i incr] [-l num]
          [-n format] [-s sep] [-v startnum] [-w width] [file]
```

一方macOSの`nl`では、`--version`オプションをサポートしていないので、一意に識別することはできません。

## Manpage

Manpageの記述を見ることでも、`nl`の種類を判断することができます。Manpageが存在しない環境では表示されませんが、ほとんどのLinuxやmacOSで表示できるはずです。`man nl`と実行すれば`nl`のManpageを読むことができます。著作権やバージョンの記述は下の方に記載されていますので、`tail`を用いて最後の10行を表示してみましょう。

``` console:Ubuntu&#x20;20.04&#x20;LTS
$ man nl | tail -n 10
       Copyright © 2018 Free Software Foundation, Inc.   License  GPLv3+:  GNU
       GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
       This  is  free  software:  you  are free to change and redistribute it.
       There is NO WARRANTY, to the extent permitted by law.

SEE ALSO
       Full documentation at: <https://www.gnu.org/software/coreutils/nl>
       or available locally via: info '(coreutils) nl invocation'

GNU coreutils 8.30              September 2019                           NL(1)
```
こちらを見てもUbuntuの`nl`はcoreutilsであることが確認できました。

```console:macOS&#x20;Catalina
% man nl | tail -n 10
STANDARDS
     The nl utility conforms to IEEE Std 1003.1-2001 (``POSIX.1'').

HISTORY
     The nl utility first appeared in AT&T System V Release 2 UNIX.

BUGS
     Input lines are limited to LINE_MAX (2048) bytes in length.

BSD                            January 26, 2005                            BSD
```
macOSはManpageのフッターを見ることで、BSD系の`nl`であることが確認できました。

# コマンドの識別

コマンドは絶対パスではなくコマンド名だけで呼び出すことがほとんどです。しかし一つの環境で複数の`nl`がインストールされている場合など、どのコマンドが呼び出されているのかわからなくなることがあります。このような場合には`which`を利用することで、デフォルトでどのコマンドが実行されているのかを調べることができます。

```` console
$ which nl
/usr/bin/nl
````

これで`/usr/bin/nl`が実行されることがわかります。さらに詳細を知りたい場合は、先ほどの例にならい`/usr/bin/nl --version`を実行してバージョンなどを調べてみると良いでしょう。

実行されるコマンドはシェルの環境変数である`$PATH`に基づいて選ばれています。詳しくは説明しませんが、この環境変数を変更することで、実行するデフォルトのコマンドを変更することができます。


----

# 補足
この記事シリーズでは特に断りのない限り、GNU coreutilsの`nl`を使用します。

macOSでcoreutilsの`nl`を利用したい場合は、`brew`などを利用してインストールできます。デフォルトでは`gnl`という名前のコマンドとしてインストールされ、もとのBSD系の`nl`と使い分けることができます。他のcoreutilsのコマンドも頭に`g`をつければ使うことができます。

```console
% brew install coreutils
(略)
% gnl --version
(略)
```