プログラマ 福重 伸太朗 〜基本へ帰ろう〜 このページをアンテナに追加 RSSフィード

2010-03-19

いまさら聞けない、#! で始まる1行目の名前とenv指定時の挙動

f:id:japanrock_pg:20100322115027p:image

シェルスクリプトであれば1行目に #!/bin/sh と書くと思うのですが、1行目の名前や挙動についてよく分かってなかったので調べてみました。


名前

シバン (Unix) - Wikipedia

シバンまたはシェバン (shebang) とはUNIXスクリプトの#!から始まる1行目のこと。起動してスクリプトを読み込むインタプリタを指定する。 hash-bangまたはsharp-bangとも言うが、後者を縮めたshebangという呼び方が一般的かつシンプルである。

シバンというのですね。

UNIXの部屋 コマンド検索:シェバング (*BSD/Linux)

この「#!」のことを「シェバング」(shebang) と呼ぶ。また、この行全体を「シェバング行」と呼ぶこともある。シェバングの語源は「sharp bang」「shell bang」など、いくつかあるようだ。


なぜ、#! なのか

UNIXの部屋 コマンド検索:シェバング (*BSD/Linux)

コンピュータが直接解釈できるのはマシン語だけである。しかし UNIX では、ファイルの先頭 2バイトが「#!」であった場合は、その後に記述されている別のコマンドを実行しようとする。


env を使う場合の挙動について

シバン (Unix) - Wikipedia

/usr/bin/env を使えばパスを直接指定する必要はなくなるが、インタプリタコマンドライン引数としてオプションを渡すことができない。

インタプリタコマンドライン引数としてオプションを渡すことができない。」とはどういうことでしょうか。Rubyで実験してみます。


rubyコマンドラインオプションを渡せない実験

'-v'' オプションを使って実験します。

envを使わない場合
% echo '#!/usr/local/bin/ruby -v' > hoge
% chmod 755 hoge
% ./hoge
ruby 1.8.6 (2009-06-08 patchlevel 369) [i686-linux]
envを使う場合
% echo '#!/bin/env ruby -v' > hoge
% chmod 755 hoge
% ./hoge
/bin/env: ruby -v: そのようなファイルやディレクトリはありません

'ruby -v 'というもので1つの引数となっているようです。念のため、#!/bin/env ruby でちゃんとrubyが使えるか実験です。

% echo '#!/bin/env ruby' > hoge
% echo 'puts "Hello World!"' >> hoge
% chmod 755 hoge
% ./hoge
Hello World!

rubyコマンドラインオプションを渡せない理由

[ruby-list:41934] Re: Cygwin での #!/bin/env ruby -Ks

多くのOSでは#!記法引数を一つしか受け付けないので、オプションを付けて呼び出したい時にはenvは使えません。envを使うとrubyインタプリタをPATHから検索してくれるので便利なこともあるんですけどね。


実験環境

OS CentOS release 5 (Final)
Ruby 1.8.6 (2009-06-08 patchlevel 369) [i686-linux]

おまけ:Mac OS X 10.6.2 でやったら env でも複数の引数が渡せた

envを使わない場合
% echo '#!/opt/local/bin/ruby -v' > hoge
% chmod 755 hoge
% ./hoge
ruby 1.8.7 (2010-01-10 patchlevel 249) [i686-darwin10]
envを使う場合
% echo '#!/usr/bin/env ruby -v' > hoge
% chmod 755 hoge
% ./hoge
ruby 1.8.7 (2010-01-10 patchlevel 249) [i686-darwin10]

env でも ruby -v が実効できました。Macはうまいことやってくれているのですかね。


おまけ:実験環境

OS Mac OS X 10.6.2

おまけ2:不正なシバンが無視される場合

不正なシバンが無視される場合があります。

#!/opt/local/bin/rubyfoooooooooooo                                                   
puts "test"

以下の結果を見ると、コマンドラインインタプリタを指定するとシバンを無視するようです。

% ruby shebang_test.rb 
test
%
% ./shebang_test.rb 
zsh: ./shebang_test.rb: bad interpreter: /opt/local/bin/rubyfoooooooooooo: no such file or directory

しかし、以下の例は無視されません。

#!/opt/local/bin/hogefugabazfoooooooooo
puts "test"

実行結果は「Can't exec」になります。シバンに ruby の文字が有るか無いかで判断しているようです。

% ruby shebang_test.rb 
shebang_test.rb:1: Can't exec /opt/local/bin/hogefugabazfoooooooooo (fatal)

この辺り、Twitterで少しやりとりがありました。


おまけ2:実験環境

OS Mac OS X 10.6.2

まとめ

誤解している部分もあるかと思いますので、ツッコミお願いします><

なかだなかだ 2010/03/19 20:18 Macというか最近のものを除くBSDの動作ですね

japanrockjapanrock 2010/03/19 20:51 >なかださん
コメントありがとうございます!
MacはBSD系UNIXベースなのですね( http://ja.wikipedia.org/wiki/Mac_OS_X )。そして「最近のものを除くBSDの動作」なのですね。
「最近のものを除くBSD」というのは、「最近のBSD」はevnに複数の引数が渡せないということでしょうか?

otnotn 2010/03/19 23:30 このあたりの話は、man 2 execve に書いてあります。

>Macというか最近のものを除くBSDの動作ですね
そもそも#!が導入されたのはBSDからだと思いますけど、4BSDとかの頃は引数は1つだったような記憶がありますけど。

japanrockjapanrock 2010/03/23 13:20 id:otnさん
コメントありがとうございます! man 2 execveにいろいろ書かれていますね!CentOSで見たところ、
-execve() は、filename によって指定されたプログラムを実行する。 filenameは、バイナリ実行形式か、"#! interpreter [arg]" という形式の行で始まるスクリプトでなければならない。後者の場合、interpreter は適切な実行ファイルのパス名でなければならず、それ自身がスクリプトであってはならない。そして それは interpreter [arg] filename の形で呼び出される。
- #! 実行形式のシェル・スクリプトの1行目には最大 127 文字が許されている。
など書いてありますね。また、Macでみたところ、
-#! interpreter [arg ...]
となっており、argを複数渡せる記述になっていました。

あと、man 2 という記述の仕方を初めて知りました。ありがとうございます! 2 の意味はセクションという意味なのですね。
http://sonic64.com/2005-08-09.html
-1 だれもが実行できるユーザコマンド
-2 システムコール、つまり、カーネルが提供する関数
-3 サブルーチン、つまり、ライブラリ関数
-4 デバイス、つまり、/dev ディレクトリのスペシャルファイル
-5 ファイルフォーマットの説明、例 /etc/passwd
-6 ゲーム(説明不要だろうネ)
-7 その他 例: マクロパッケージや取り決め的な文書
-8 システム管理者だけが実行できるシステム管理用のツール
-9 Linux 独自のカーネルルーチン用のドキュメンテーション
-n 新しいドキュメンテーション:よりふさわしい場所に移動されるだろう
-o 古いドキュメンテーション 猶予期間として保存されているもの
-l 独自のシステムについてのローカルなドキュメンテーション

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/japanrock_pg/20100319/1268968887