【Unix／Linux】異次元の生産性をコマンドラインで

# はじめに

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

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

### 参考URL

自分のブログから転載しました。
[異次元の生産性をコマンドラインで](https://daimaohsx.dip.jp/wordpress/2021/01/05/to-search-for-a-string-in-a-text-file-and-get-information-quickly/)

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

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

```bash: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）ことを知り、驚きました。開発者の発想は同じところに行き着くのですね。

# シェルスクリプトを探索してみる

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

```:whichコマンド
$ which which
/usr/bin/which
$ file /usr/bin/which
/usr/bin/which: POSIX shell script, ASCII text executable

[Ubuntu 20.10]
```

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

## シェルスクリプトを見つけるには

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

```bash:findとfgrepを組み合わせる
% 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コマンドでカウントします。

```bash:さらにwcを組み合わせる
 % find /usr/bin -exec file {} \; | fgrep 'shell script' | wc -l
      85

[macOS 11]
```

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

```bash:macOSの/etcにシェルスクリプトはいくつあるか
% cd /etc
% sudo find . -exec file {} \; | fgrep 'shell script' | wc -l
Password:
      16

[macOS 11]
```
```bash:Ubuntuの/etcにシェルスクリプトはいくつあるか
$ sudo find /etc -exec file {} \; | fgrep 'shell script' | wc -l
[sudo] xxxx のパスワード: 
132

[Ubuntu 20.04]
```

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

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

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

```bash:ファイル名を取り出す
 % 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を使用して次々とファイルの中身を参照できます。
途中で閲覧をやめ、viを終了するには”:q![Enter]”と入力します。

```bash:次々とファイルを閲覧する
% find /usr/bin -exec file {} \; | fgrep 'shell script' | cut -d: -f1 | xargs vi
```

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

```bash:ファイルを連続して閲覧できるシェルスクリプトを生成する
% find /usr/bin -exec file {} \; | fgrep 'shell script' | cut -d: -f1 | sed -e '1,$s/$/ \\/' | sed -e '1s/^/vi /' > s.out
% sh ./s.out
```

上記は、ファイルの一覧をsedで加工し、ファイルの閲覧ができるシェルスクリプトを生成しています。
まず、ファイル名の末尾に\を加えて継続行にします。次に、行の先頭に”vi”をつけてファイルに書き出し、シェルスクリプトとして起動したのでした。書き出したファイルのイメージが以下です。

```bash:s.outのイメージ
vi /usr/bin/what-patch \
/usr/bin/autopoint \
/usr/bin/ikdasm \
/usr/bin/installvst \
/usr/bin/foo2hp2600-wrapper \
＜略＞
```

# さらに特定の条件にマッチするシェルを抽出する

これまでに抽出したシェルスクリプト群から、「Usage:」（使用法：）が含まれるものを抽出してみます。

```bash:Usageのあるシェルスクリプトを選び出す
% find /usr/bin -exec file {} \; | fgrep 'shell script' | cut -d: -f1 | xargs grep -il 'usage:' | uniq       
/usr/bin/bzmore
/usr/bin/phpize
/usr/bin/fddist
＜略＞

[macOS 11]
```

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

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

次に、Usageのあるシェルスクリプトの数を数えてみましょう。

```bash:Usageのあるシェルスクリプトの数
% find /usr/bin -exec file {} \; | fgrep 'shell script' | cut -d: -f1 | xargs grep -il 'usage:' | uniq | wc -l
      52
```

macOS 11(Big Sur)の/usr/binにあるシェルスクリプト85本中、「Usage」が記述されているものは52本という結果でした。

シェルスクリプトを作成するにあたり、このように特定のキーワードが含まれるものを検索して、参考にすることができると、効率的に作業できる場合があります。

# さいごに

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