シェルスクリプトのシバン(Shebang)にシェル以外のコマンドを記述したらどうなるのか?
はじめに
UNIX 上で動作するスクリプトは、1 行目に #!
から始まる 1 行を記述します。
これによりスクリプトの実行系を認識させることができます。
シェルスクリプトの場合は #!/bin/sh
、 #!/usr/bin/bash
、 #!/usr/bin/env bash
と記述します。
Ruby スクリプトの場合は #!/usr/bin/ruby
、 #!/usr/bin/env ruby
と記述することが多いでしょう。
この 1 行目の記述のことを「 シバン 」とか「 Shebang 」と呼びますが、今ひとつこのあたりの仕組が理解できてなかったため、シバンでいろいろ遊んでみることにしました。
検証環境
$ uname -moi
x86_64 MacBookPro16,1 Darwin
$ bash -version | head -n 1
GNU bash, バージョン 5.0.17(1)-release (x86_64-apple-darwin19.4.0)
(実験 1) #!/bin/rm
と記述し実行してみる
突然ですが、 シバン の挙動を理解するために実験をしてみます。
一行目に #!/bin/rm
とだけ記述されたシェルスクリプトを作成してみます。
# 作成
$ cat <<EOF > test.sh
#!/bin/rm
EOF
# 内容が正しいかを確認してみる
$ cat test.sh
#!/bin/rm
# 実行権限を付与
$ chmod +x test.sh
# カレントディレクトリを確認
$ ls -la
total 8
drwxr-xr-x 3 root root 96 Sep 27 13:46 .
drwxr-xr-x 1 root root 4096 Sep 27 13:34 ..
-rwx--x--x 1 root root 10 Sep 27 13:47 test.sh
作成したスクリプトを以下のように実行してみます。
$ ./test.sh
実行後、カレントディレクトリを確認してみると、 test.sh
というスクリプトがなくなっています。
$ ls -la
total 4
drwxr-xr-x 2 root root 64 Sep 27 13:48 .
drwxr-xr-x 1 root root 4096 Sep 27 13:34 ..
これはなぜでしょう。
./test.sh
を実行した結果、 /bin/rm ./test.sh
が実行されたようです。
シバン付きのスクリプトを実行した場合、 シバンに書かれたコマンドが実行され、「スクリプト名」が引数として渡される のではないかと考えられます。
(実験 2) #!/bin/echo
と記述し実行してみる
追加の実験をしてみます。
一行目に #!/bin/echo
とだけ記述されたシェルスクリプトを作成してみます。
# 作成
$ cat <<EOF > test.sh
#!/bin/echo
EOF
# 内容が正しいかを確認してみる
$ cat test.sh
#!/bin/echo
# 実行権限を付与
$ chmod +x test.sh
作成したスクリプトを、いくつかのコマンドライン引数付きで実行してみます。
$ ./test.sh aa bb ccc
./test.sh aa bb ccc
ここから、以下のようなコマンドが実行されたようです。
$ /bin/echo ./test.sh aa bb ccc
改めて #!/bin/bash
の意味を考える
改めて #!/bin/bash
の意味を考えてみます。
例えば、 シバン付きの test.sh スクリプトを以下のように実行した場合を考えてみます。
$ ./test.sh aa bb ccc
これは、以下のようなコマンドが実行されることとなります。
/bin/bash ./test.sh aa bb ccc
このように整理できると、シェルスクリプトの中で ${0}
という組み込み変数が 実行されたスクリプトファイルのパス を示している仕組みが理解できるようになります。
ちなみに御存知の通り、コマンドライン引数を表す変数の値は、 ${1}
は aa 、 ${2}
は bb 、 ${3}
は ccc となります。
(実験 3) #!/bin/echo first
と記述し実行してみる
#!/usr/bin/env bash
の意味についても考えてみましょう。
その前に実験です。
先程の echo
コマンドをシバンとして設定したスクリプトを多少触ってみます。
一行目に #!/bin/echo first
と記述されたシェルスクリプトを作成してみます。
$ cat <<EOF > test.sh
#!/bin/echo first
EOF
$ chmod +x test.sh
以下のように引数を付与して実行してみます。
$ ./test.sh xx yy zz
実行結果は以下の通りとなります。
first ./test.sh xx yy zz
これはつまり、 /bin/echo first ./test.sh xx yy zz
というコマンドが実行されたと解釈できます。
#!/usr/bin/env bash
も同様です。
#!/usr/bin/env bash
がシバンに設定された test.sh
スクリプトを実行すると、 /usr/bin/env bash ./test.sh
というコマンドが実行されることとなります。
補足: env
コマンドとは?
env
コマンドを通すと $PATH
環境変数の設定を使って bash
コマンドを探します。
/bin/bash
にコマンドが配置されていない環境でも動作しますし、 bash
が複数インストールされている場合(たとえばバージョン違い)にも、最適なもの ( $PATH
の設定を使って最初に見つかったもの ) を検索してくれます。
したがって、僕個人の意見としてシバンは #!/bin/env
よりも #!/usr/bin/env bash
をおすすめします。
(実験 4) #!./test.sh xx yy zz
と記述し実行してみる
最後の実験です。シバンの引数を 2 つ以上指定した場合はどのような挙動になるでしょう。
シバンにシェルスクリプトを指定し実行することで、この挙動をより深く理解することができます。
test.sh
$ cat <<'EOF' > test.sh
#!/bin/bash
echo "\$0 = $0"
echo "\$1 = $1"
echo "\$2 = $2"
echo "\$3 = $3"
EOF
$ chmod +x test.sh
作成した test.sh
スクリプトに引数を付与して実行してみます。
引数が表示されるはずです。
$ ./test.sh xx yy zz
$0 = ./test.sh
$1 = xx
$2 = yy
$3 = zz
test.sh
をシバンに指定した hoge.sh
を用意します。
$ cat <<'EOF' > hoge.sh
#!./test.sh xx yy zz
EOF
$ chmod +x hoge.sh
作成した hoge.sh
スクリプトに引数を付与して実行してみます。
すると面白いことがわかります。
$ ./hoge.sh 1 2 3
$0 = ./test.sh
$1 = xx yy zz
$2 = ./hoge.sh
$3 = 1
結果から、実行されたコマンドは以下のようなものだったことがわかります。
ポイントは "xx yy zz"
の部分です。
シバンに指定された引数はダブルクォートで囲まれたように 1 つの引数として扱われています。
./test.sh "xx yy zz" ./hoge.sh 1 2 3
シバンに 2 つ以上の引数を指定する場合には注意が必要そうです。
ひとこと
多少はシバンの仕組みが整理できたでしょうか。
ディスカッション
コメント一覧
まだ、コメントがありません