みなさん、シェル使ってますか?私は毎日使ってます。
「シェルとは?」といった記事はたくさんあるのでここでは深く触れませんが、ざっくり言うと「コマンドでカーネルと対話的なインターフェースを提供する仕組み」といった感じでしょうか。
そんなシェルについてですが、「シェルスクリプトの中で現在利用しているシェルを確認する方法」が知りたくてちょっと調べてみました。
今回の動作確認環境
- Vagrant 2.2.14
- CentOS 8
※Dockerを利用すると $SHELL
(ログインシェル(デフォルトシェル))の表示の際に期待した動作とは異なりました。Dockerの場合は動作に必要な最小構成になっていることがあったり、ホストPC上で動作している仕組みのためにこのようなことが起こるのかもしれません。そういったことを踏まえて今回はVagrantを使ってみてます。(このあたりは詳しく調べきれてないところではあります)
「LinuCレベル1」の試験範囲に「GNUとUnixのコマンド」という章があり、試験勉強をしているとその中で以下のコマンドで「現在のシェル」を知ることができると説明されています。
$ echo $SHELL
/bin/bash
完。。。
と思うじゃないですか。
でも、この $SHELL
は想定とは違う動きをします。
↑のコマンドはCentOS8の環境で実行したんですが、CentOS8の初期状態は利用できるシェルが少ない状態です。
以下のコマンドで確認すると sh
と bash
のみです。
$ cat /etc/shells
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash
しかも sh
は bash
へのシンボリックリンクになっているので実質1種類と言えます。
$ ls -l /usr/bin/sh
lrwxrwxrwx. 1 root root 4 May 11 2019 /usr/bin/sh -> bash
$ ls -l /bin/sh
lrwxrwxrwx. 1 root root 4 May 11 2019 /bin/sh -> bash
なので、 zsh
、 ksh
、 tcsh
をインストールします。
$ sudo yum install zsh ksh tcsh -y
このように利用可能なシェルが増えました。
$ cat /etc/shells
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash
/usr/bin/zsh
/bin/zsh
/bin/csh
/bin/tcsh
/usr/bin/csh
/usr/bin/tcsh
/bin/ksh
/bin/rksh
/usr/bin/ksh
/usr/bin/rksh
それでは改めて $SHELL
を見てみましょう。
zsh
に変更して。。。おや?
$ zsh
% echo $SHELL
/bin/bash
ksh
に変更して。。。おやおや?
$ ksh
$ echo $SHELL
/bin/bash
tcsh
に変更して。。。おやおやおや?
$ tcsh
$ echo $SHELL
/bin/bash
期待している動作と異なりますね。
$SHELL
は現在のシェルではなく、どうやらログインシェル(デフォルトシェル)を表示しているようです。
こういった場合に使えるのが $0
です。
ログイン直後でデフォルトシェルが bash
の場合は。
$ echo $0
-bash
zsh
に変更すると。
$ zsh
% echo $0
zsh
ksh
に変更すると。
$ ksh
$ echo $0
ksh
tcsh
に変更すると。
$ tcsh
$ echo $0
tcsh
完。。。といった感じですね。
それではタイトルのとおり、「シェルスクリプトの中で現在利用しているシェル」を確認していこうと思います。
まずは $0
を表示するスクリプトを記述したファイルを作成します。
$ echo 'echo $0' > show_shell.sh
それでは bash
で実行してみます。
$ bash show_shell.sh
show_shell.sh
ファイル名が表示されました。
そりゃそうです。 man bash
を開くと ARGUMENTS
のところに $0 is set to the name of the file
と明記されています。
ついこの前書いた「bashの特殊変数の説明を読んでもよくわからなかったので試してみた」という記事の中でも、 $0
は「スクリプトファイル名」と説明していますし。。。
困りましたね。どのシェルで呼び出してもすべて同じ結果です。
$ zsh show_shell.sh
show_shell.sh
$ ksh show_shell.sh
show_shell.sh
$ tcsh show_shell.sh
show_shell.sh
そこで登場するのが /proc
ディレクトリです。
/proc
配下のファイルには色々な情報が詰まっている特別なディレクトリで、 /proc/cpuinfo
や /proc/meminfo
なんかは利用したことがあるんじゃないでしょうか。
/proc
配下のファイルは通常のファイルとは異なる形式になっていて、ファイルサイズが0だったり、作成日時が現在時刻だったりと不思議な作りになっています。
$ ls -l /proc/cpuinfo
-r--r--r--. 1 root root 0 Jul 9 16:56 /proc/cpuinfo
$ ls -l /proc/meminfo
-r--r--r--. 1 root root 0 Jul 9 16:56 /proc/meminfo
/proc
配下のファイルを見てみると、数字のディレクトリが存在しています。これはプロセスIDと一致しています。
$ ls /proc/
1 1902 25 441 636 725 execdomains loadavg softirqs
10 2 26 442 637 8 fb locks stat
11 20 27 443 656 9 filesystems mdstat swaps
12 21 28 444 659 acpi fs meminfo sys
13 22 29 445 661 asound interrupts misc sysrq-trigger
14 23 3 446 662 buddyinfo iomem modules sysvipc
141 24 30 447 663 bus ioports mounts thread-self
142 24687 31 448 666 cgroups irq mtrr timer_list
143 24697 32 48 667 cmdline kallsyms net tty
144 24701 4 535 689 consoles kcore pagetypeinfo uptime
146 24705 416 561 691 cpuinfo key-users partitions version
147 24711 418 6 692 crypto keys sched_debug vmallocinfo
16 24712 420 619 699 devices kmsg schedstat vmstat
17 24844 421 624 711 diskstats kpagecgroup scsi zoneinfo
18 24874 422 625 715 dma kpagecount self
19 24882 440 635 724 driver kpageflags slabinfo
「bashの特殊変数の説明を読んでもよくわからなかったので試してみた」の中で「現在実行しているシェルのPID」は $$
で表現されると説明しました。
これを応用して、 ls /proc/$$
を実行してみましょう。すると現在のプロセスIDで動いている固有の情報が詰まったファイルの一覧が表示されます。
$ ls /proc/$$
attr cpuset loginuid numa_maps sched status
autogroup cwd map_files oom_adj schedstat syscall
auxv environ maps oom_score sessionid task
cgroup exe mem oom_score_adj setgroups timens_offsets
clear_refs fd mountinfo pagemap smaps timers
cmdline fdinfo mounts patch_state smaps_rollup timerslack_ns
comm gid_map mountstats personality stack uid_map
coredump_filter io net projid_map stat wchan
cpu_resctrl_groups limits ns root statm
この中で注目すべきファイルは exe
です。
man proc
を見てみると、 /proc/[pid]/exe
は this file is a symbolic link containing the actual pathname of the executed command.
のように説明されています。
「実行されたコマンドの実際のパス名を含んだファイル」ということは、 /proc/$$/exe
を ls
してみると。。。現在実行しているシェルへのシンボリックリンクが表示されました!
$ ls -l /proc/$$/exe
lrwxrwxrwx. 1 vagrant vagrant 0 Jul 9 17:13 /proc/24712/exe -> /usr/bin/bash
シンボリックリンクのリンク先の名前を表示する readlink
というコマンドを組み合わせると。。。来ました!これが求めていたものです!!
$ readlink /proc/$$/exe
/usr/bin/bash
事前に準備していた show_shell.sh
を書き換えます。
$ echo 'readlink /proc/$$/exe' > show_shell.sh
それぞれのシェルで実行してみると、「シェルスクリプトの中で現在利用しているシェル」を表示することができました。
$ bash show_shell.sh
/usr/bin/bash
$ ksh show_shell.sh
/usr/bin/ksh93
$ zsh show_shell.sh
/usr/bin/zsh
$ tcsh show_shell.sh
/usr/bin/tcsh
この /proc/$$/exe
は shebang
にも有効です。
実行可能なファイルの場合、シェルスクリプトは shebang
を元にどのシェルを利用するのかを決定するので、 show_shell.sh
次のように書き換えて試してみます。
実行権限を付与しておいて。
$ chmod +x show_shell.sh
まずは bash
の場合。このように shebang
を書いて。
$ cat show_shell.sh
#!/usr/bin/bash
readlink /proc/$$/exe
実行。
$ ./show_shell.sh
/usr/bin/bash
zsh
の場合も同様に shebang
を書き換えて。
$ cat show_shell.sh
#!/usr/bin/zsh
readlink /proc/$$/exe
実行。
$ ./show_shell.sh
/usr/bin/zsh
ここで、さらに実験です。実行可能なファイルで shebang
が書かれていない場合はどうなるのでしょうか。
$ cat show_shell.sh
readlink /proc/$$/exe
CentOS8の場合は /usr/bin/bash
が選択されました。
$ ./show_shell.sh
/usr/bin/bash
shebang
が無い場合の実行可能ファイルのシェルの決定ロジックについては複雑なようで、「実行可能ファイルの場合はどのシェルが利用されるかわからないような曖昧な状態にせず shebang
を書いて意図どおりのシェルが選択されるようにしましょう」ということみたいですね。
参考URL
- proc(5) — Linux manual page
- /proc/(数字)ディレクトリ
- /proc/<pid>/ディレクトリに存在するファイル
- Bash script execution with and without shebang in Linux and BSD
- Shell script working fine without shebang line? Why? [duplicate]
- 使っているシェルを調べる方法
- 現在の shell を shellscript の中から確認する(Linux, BSD 両対応)
- 現在使っているシェルの名前を知る方法
- 現在実行中のシェルを知る方法
- 現在利用しているシェルの種類を確認する(GNU BashなのかZ Shellなのか)
- 現在使用しているシェルを確認