Bash 上で使えるコマンド一覧を取得する!
ふと、Bash 上で使えるコマンドって全部でどれだけあるんだろう? と思い、一覧での取得方法を調べてみた。
ネットで見つけたやり方
↑ ドンズバな質問と回答が載っているページを見つけた。以下のコマンドで実行可能なコマンドを一覧で表示できるようだ。
$ for x in ${PATH//:/ }; do ls -1 $x; done | sort | uniq
環境変数 PATH
は、コロン :
区切りで複数のディレクトリパスが羅列されている。コレを変数展開して、スペース区切りに直している。
${【変数】//【検索文字列】/【置換文字列】}
…//
で登場箇所を全て置換する- 参考 : GPソフト Wiki - シェルスクリプト in bash
for … in
はデフォルトでスペースを区切りとして要素を分割するので、スペース区切りとなった PATH
の内容を1要素ずつ取り出し、変数 x
に割り当てている。
そしてこれを ls -1
(1列表示) の引数に渡し、PATH
に書かれたディレクトリ配下のファイルを1ファイル1行ずつ表示している。イメージ的には以下のようなことを実現している。
$ ls -1 /usr/local/bin
$ ls -1 /usr/bin
$ ls -1 /usr/sbin
これらの結果が for
コマンド一発の実行結果として done
まで続き、その結果を sort
して、uniq
で重複を削り、完成、となっている。
いまさら改めて気付いたが、自分が「コマンド」と認識しているモノのほぼ全ては、環境変数 PATH
で指定したディレクトリ内に実行可能ファイルがあるだけなのだ。which
コマンドで所在が調べられるのだが、そりゃそうだ。
ということは、環境変数 PATH
に羅列されたディレクトリ内のファイルを一挙に表示すれば、それが「コマンド一覧」とほぼ同義になるワケだ。
上述の記事でも触れられているが、Bash 組み込みコマンドは $ bash -c help
、エイリアスは $ alias
で確認することになる。また、~/.bash_profile
などで定義した関数についてはこのコマンドでは取得できない。
自分の環境で上手く動かなかった
さて、さきほどのコマンドだが、自分の環境では上手く動かなず、一部エラーが出力されてしまった。コレは、環境変数 PATH
に、スペースが入ったディレクトリパスが含まれていたせいである。
# PATH を出力したところの抜粋
$ echo $PATH
/usr/local/my original commands directory/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin
こんな風に my original commands directory/
というディレクトリパスが入っていた。
先程も書いたが、for
コマンドはスペースを区切り文字と見なして要素を分割するのがデフォルトの挙動だ。つまり先程の例だと、
/usr/local/my
original
commands
directory/bin
/usr/local/bin
……
という風に分割されてしまったので、$ ls -1 original
とか言われてもワケ分かんねえよ、というエラーが出ていたのだ。
コマンドを修正する
そこで、for
コマンドに要素を分割させる時、$PATH
でいうコロン :
の位置で分割させるため、コマンドを少し修正してみる。
for
コマンドが区切り文字として認識する文字を変更するには、$IFS
という変数で違う文字を指定してやれば良い。
今回は、IFS に改行コード \n
を指定して、「改行で1要素と区切る」ようにしてみる。
以下の記事でも触れられているとおり、バックアップ用の変数を用意しておくと、処理後に元の IFS に戻せるよ、とのこと。
- 参考 : Bash 区切り文字の変更
まずはこんなコードになる。
# 元の IFS の内容を退避しておく
$ IFS_BK=$IFS
# 区切り文字を改行コードにする
$ IFS=$'\n'
# TODO : ココで処理…
# 区切り文字を元に戻す
$ IFS=$IFS_BK
次に、$PATH
をコロン区切りから「改行区切り」にしてみる。コレは tr
コマンドで置換すると実現できる。
$ echo $PATH | tr ':' '\n'
ということで、for
コマンドに以下のように組み込んでやるのが良いだろう。
IFS_BK=$IFS
IFS=$'\n'
# $() でコマンドの実行結果を渡す
for x in $(echo $PATH | tr ':' '\n') ; do
# スペース区切りのパスも解釈させるためダブルクォートで囲む
ls -1 "$x"
done
IFS=IFS_BK
コレを1行にまとめて、以下のようにすれば、一発で動かせる。
$ IFS_BK=$IFS && IFS=$'\n' && for x in $(echo $PATH | tr ':' '\n') ; do ls -1 "$x" ; done | sort | uniq && IFS=$IFS_BK
コレで Windows GitBash でも MacOS でも Linux CentOS でもコマンド一覧を出力できた。いっぱいあるのね〜ん
2019-02-14 追記 : compgen
なるコマンドがありました。