現在の shell を shellscript の中から確認する(Linux, BSD 両対応)
現在の shell(以下、current shell) を確認する方法として真っ先に思い浮かぶのは以下だと思います。
$ echo $SHELL /bin/bash
ただ、これは不正解で、$SHELL はあくまでも login shell ですので、
login shell と current shell が違う場合は正しい値を返してくれません。
私の場合ですと、ローカル環境で常時利用しているのは zsh ですが、
login shell は bash です(zsh を login shell にすると重いと教えていただいたことがあるので)。
このようなケースでも正しい値を返す方法が以下です。
$ echo $0 -zsh
shell 上では $0 に current shell が格納されています。
これで解決すれば良いのですが、shellscript 内で確認しようと思うと話しは変わってきます。
shellscript 内で $0 には、script 名が格納されています。
shell 上での利用よりも、shellscript 内での利用頻度の方が高いでしょうからこれでは困ります。
色々調べていたら、現在使っているシェルの名前を知る方法 - 揮発性のメモ を発見しました。
いろいろ参考になりました、ありがとうございます。
readlink /proc/$$/exe
の説明をすると、
Linux の /proc/PID 以下には、プロセスに関する情報が色々入っていて、
- $$ で shellscript 自身の PID を取得して、
- シンボリックリンクの実体のパスを返す readlink に、
- コマンドのパスのシンボリックリンクが含まれている /proc/$$/exe を渡して、
- shellscript を実行している shell を取得する
となります。
これで解決かと思いましたが、以下の欠点があります。
- /proc が Linux にしかない(たぶん)
- 実行した時の shell になるので、current shell とは違う
2点目について説明すると、
shebang に #!/bin/bash と指定してスクリプトを実行したり、
bash /path/to/script のように実行すると、zsh を使っていても /bin/bash が返ってくる、ということです。
今回は、あくまでも current shell を取得したいのでこれでは要件を満たせません。
解決策
実行した shellscript の親プロセスは current shell になっているのではないか、
と当たりをつけて実験したらできました。
1つずつ説明すると、
_PID=$$
で Shellscript 自身の PID を取得して、
_PPID=$(ps -o ppid -p $_PID | tail -n 1)
で、Shellscript 自身の親PID(PPID)を出力しています。
これを更に分解すると、
$ ps -o ppid -p $_PID PPID 12345
のような出力になるので、
tail -n 1
で 2 行目だけ出力しているようにしています。
--no-headers を使っていない理由は BSD の ps になかったからです。
あとは、親PIDが取れたので、
ps -p $_PPID
で PID 指定で ps しています。
これで 実行した shellscript の親プロセスの情報を取得出来ました。
zsh を使っている状態でこれを実行すると、
$ ./get_current_shell.sh PID TTY TIME CMD 12345 pts/1 00:00:00 zsh
となります。
header を消したければ、同じ要領で tail をかませればよいでしょう。
あとは、grep -qs zsh にマッチすれば zsh、grep -qs bash にマッチすれば bash
といった判定が可能になります。
まとめ
login shell と current shell が違う場合や、
実行時の shell と current shell が違う場合でも、current shell を返す方法を示しました。
それほど使い道はないかもしれませんが、
.bashrc や .zshrc を読み直させたり追記したいときに、
login shell と current shell が違う場合でも、適切なファイルに対して処理が行えるでしょう。
Linux だけでなく BSD でも動くようにしましたので、汎用的に使えるのではないかと思います。
追記1
@hnakamur2 さんに、awk で 2行目をだけを出力するよりも、
tail -n 1 のほうがシンプルだとご指摘いただきましたので修正しました。
ご指摘ありがとうございます!
Linux, BSD で検証して動作することを確認済みです。
# 訂正前 _PPID=$(ps -o ppid -p $_PID | awk 'NR==2 { print }') # 訂正後 _PPID=$(ps -o ppid -p $_PID | tail -n 1)
追記2
@songmu さんからご指摘頂きました。
ありがとうございます!
shellscript(親shellscript) の中で shellscript(子shellscript) を呼び出す場合は、
子shellscript から見た親プロセスは、親shellscript となってしまいます。
したがって、正しく動作しません。
現状では、まだ解決できていませんが、
current shell 判定処理を関数化して別ファイルにし、
. get_current_shell.sh
get_current_shell
のように読み込んで、子shellscript から呼ばないようにするしかありません。
解決できたら追記致します。