異次元の生産性をコマンドラインで

コマンドラインの技は合気道や空手のような武道と似ています。習得するには時間がかかりますが、習得すると圧倒的なパワーとなります。

たいていのことは統合開発環境IDEで可能です。しかし、細かな条件を指定してファイルを絞り込んだり、それらのファイルを横串にして文字列検索したりということはIDEでは手に余ります。ファイルマネージャでも困難です。
本稿ではコマンドラインでしかできないような、ワザの一端を紹介したいと思います。

ディレクトリ階層を下がって目的のファイルを見つける

C言語の”NULL”の定義がどのインクルードファイルでどのように行われているかを知りたいとします。

$ find /usr/include -name '*.h' -exec grep '#define NULL' {} /dev/null \;
/usr/include/X11/Xresource.h:#define NULLQUARK ((XrmQuark) 0)
/usr/include/X11/Xresource.h:#define NULLSTRING ((XrmString) 0)
/usr/include/linux/uuid.h:#define NULL_UUID_LE					\
/usr/include/unicode/utypes.h:#define NULL    nullptr
/usr/include/unicode/utypes.h:#define NULL    ((void *)0)
/usr/include/tirpc/rpc/xdr.h:#define NULL_xdrproc_t ((xdrproc_t)0)
/usr/include/tirpc/rpc/clnt.h:#define NULLPROC ((rpcproc_t)0)

[Ubuntu 20.10]

上記では、findコマンドで/usr/includeを起点として、サフィックスが”.h”のファイルを一覧しています。そして、個々のファイルの中をgrepコマンドを用いて”#define NULL”の文字列検索しています。

私はこのfindコマンドとgrepコマンドを組み合わせた使い方を、日常的に行っています。ところが、ある日「プロダクティブ・プログラマ」(NealFold著,島田浩二監訳,夏目大訳)p.250に同じ使い方が記載されていることを知り、驚きました。開発者の発想は同じところに行き着くのですね。

同一の属性を持つファイルを一覧にする

標準コマンドの中には、シェルスクリプトで作成されているものが多くあります。”which”コマンドもそうです。

$ which which
/usr/bin/which
$ file /usr/bin/which
/usr/bin/which: POSIX shell script, ASCII text executable

whichコマンド自身でwhichコマンドがどこにあるか調べ、fileコマンドでシェルスクリプトかどうかを確かめています。

ここであなたが、シェルスクリプトを作成することになり、標準のシェルスクリプトを参考にすることになったとします。そこでまず、/usr/binにシェルスクリプトはいくつあるのか数えてみることにしましょう。

% find /usr/bin -exec file {} \; | fgrep 'shell script'
/usr/bin/zmore: POSIX shell script text executable, ASCII text
/usr/bin/bzmore: POSIX shell script text executable, ASCII text
/usr/bin/umask: POSIX shell script text executable, ASCII text
<略>
[macOS 11]

個々のファイルを”file”コマンドで調べ、fgrepで”shell script”文字列を検索することにより、シェルスクリプトを抽出しています。

抽出したシェルスクリプトの数をカウントしてみる

週出したファイルの数を、wcコマンドでカウントします。

 % find /usr/bin -exec file {} \; | fgrep 'shell script' | wc -l
      85

[macOS 11]

85個のシェルスクリプトがあることがわかりました。
ついでに、/etc下にシェルスクリプトがいくつあるかを、macOSとUbuntuで確かめてみましょう。

[macOS 11]
% cd /etc
% sudo find . -exec file {} \; | fgrep 'shell script' | wc -l
Password:
      16

[Ubuntu 20.04]
$ sudo find /etc -exec file {} \; | fgrep 'shell script' | wc -l
[sudo] xxxx のパスワード: 
132

macOSの場合、/etcに移動してからfindでサーチしているのは、macOSの/etcがシンボリックリンクだからです。

シェルスクリプトたちをエディタで開いてみる

macOSの/usr/binに戻って、抽出したシェルスクリプト郡をエディタのviコマンドで閲覧してみます。まず、シェルスクリプトのファイル名を切り出します。

 % find /usr/bin -exec file {} \; | fgrep 'shell script' | cut -d: -f1
/usr/bin/zmore
/usr/bin/bzmore
/usr/bin/umask
/usr/bin/phpize
<略>

[macOS 11]

次に、ファイル名をviに渡して閲覧します。途中で閲覧をやめ、viを終了するには”:q!”と入力します。

% find /usr/bin -exec file {} \; | fgrep 'shell script' | cut -d: -f1 | xargs vi

ここだけの話ですが、xargsを知らない頃は、次のようにしていました。

% find /usr/bin -exec file {} \; | fgrep 'shell script' | cut -d: -f1 | sed -e '1,$s/$/ \\/' | sed -e '1s/^/vi /' > s.out
% sh ./s.out

ファイル名の末尾に\を加えて継続行にし、行の先頭に”vi”をつけてファイルに書き出し、シェルから起動したのでした。書き出したファイルのイメージが以下です。

[s.outのイメージ]
vi /usr/bin/what-patch \
/usr/bin/autopoint \
/usr/bin/ikdasm \
/usr/bin/installvst \
/usr/bin/foo2hp2600-wrapper \
<略>

さらに特定の条件に合うファイルを選別する

これまでに抽出したシェルスクリプトで作成されたコマンドから、「Usage:」(使用法:)が含まれるものを抽出してみます。

% find /usr/bin -exec file {} \; | fgrep 'shell script' | cut -d: -f1 | xargs grep -il 'usage:' | uniq | wc -l
      52

[macOS 11]

「Usage」が大小文字いずれであってもマッチするように検索し、ファイル名のみを出力します。1ファイルで複数行マッチした場合、それを1行にするためにuniqコマンドを使用しています。

ちなみに、上記の場合、本当はuniqは不要です。なぜなら、grepで”l”(エル)オプションを指定しているからです。”l”は文字列がマッチしたとき、ファイル名のみ出力するオプションですが、最初にマッチした時点でそのファイルの検索が終了するからです。

シェルスクリプトすべてに「Usage」があるかと思いましたが、そうではありませんでした。

さいごに

コマンドラインの世界は、非常にクリエイティブで、常に新しい発見があります。私自身道半ばです。
本稿がみなさんの生産性を高める一助になることを願っています。