Bashが好きです。スクリプトのTry&Errorで作っていく感じが性に合っているように思います。大学院時代からちょこちょこと触り始めたので、2017年現在で6年目となりますが、Bashは奥が深く。。という言葉で逃げてしまうと永遠にその背中に追いつけないような気がするので、一つ一つ理解を深めていいけたらと思い、記事にまとめてみます。こちらの記事はパッチワーク的に書いていこうと思います。
shebangって何?
シェバン手始めにwikiで調べてみましょう*1。日本語のwikiよりも英語のwikiの方がより詳しく書かれていますね。と言うか、なんだか書くことがなくなった感がありますね笑 まあ、気を取り直してgoogle翻訳を駆使して理解していきましょう。
Unixライクなシステムにおいて、shebangで始まるプログラムとして実行されたとき、 program loader*2 はshebangをinterpreter directive*3として解析していきます。
訳が上手くないとは言え、いきなり二つも人に説明できない事柄にぶつかってしまいました。。。これまでの私は「Bashなどのシェルやcronでプログラムを実行した際に、shebangが指定されているとshebangよりも後のプログラムがshebangで指定されたプログラムとして解釈される」と理解していました。合っているような、間違っているような、微妙ですね(ーー;)
例えばこんな風に書いたらどうなるのだろうか
例えばshebangを書かないで実行権限だけつけたBashのスクリプトをBashで実行するとどうなるかと言えば、次のようになります。
Bash# cat shebang_1.sh echo shebang Bash# chmod 755 shebang1.sh Bash# ./shebang_1.sh shebang
見たまんまですね。スクリプトはBashが解釈するので「shebang」とechoされています。 次に同じようにshebangを書かないパターンとして、Rubyでやってみたらどうでしょうか
Bash# cat shebang_2.rb puts "shebang" Bash# chmod 755 shebang_2.rb Bash# ./shebang_2.rb ./shebang_2.rb: line 1: puts: command not found Bash# ruby ./shebang_2.rb shebang
こちらについても、予想通りの出力になったかと思います。putsコマンドが見つかりません。とのことですね。こちらはPathが通っていないコマンドを実行してしまった場合に見かけるエラーですね。rubyをコマンドとして指定すれば問題なく実行できるようです。では、Bashから直接Rubyのスクリプトを実行するにはどのようにすればよいのでしょうか?そうです、shebangの出番ですね!
Bash# cat shebang_3.rb #!/usr/bin/ruby puts "shebang" Bash# chmod 755 shebang_3.rb Bash# ./shebang_3.rb shebang
でもここでいちいちfull-pathを指定するのが面倒になるわけですね。「えいや」でrubyだけ指定したらどうなるか試してみましょうw
Bash# cat shebang_4.rb #!ruby puts "shebang" Bash# chmod 755 shebang_4.rb Bash# ./shebang_4.rb -bash: ./shebang_4.rb: ruby: bad interpreter: No such file or directory
当然ダメですね。さてここでinterpreterという単語が出てきましたね、日本語にすると「通訳」となるようですね。「Rubyなんて知らないよ」ということですね。だからfull-pathで指定しなきゃいけないと。。。面倒ですね。しかも、インストール先が異なるシステム間ではスクリプトの融通が利かなくなってしまいます。そこで登場するのが「env」コマンドですね。上のスクリプトは、次のように書けばよかったわけです。
Bash# cat shebang_5.rb #!/bin/env ruby puts "shebang" Bash# chmod 755 shebang_5.rb Bash# ./shebang_5.rb shebang
いい感じです。このように書けば異なるシステム間でも、スクリプトを共有できるわけです。そして、ここで落とし穴が待ち受けているわけです。そうですcronです。こちらのRubyスクリプトをcronで起動してみましょう。
Bash# crontab -ls */1 * * * * /root/work/shebang_5.rb You have mail in /var/spool/mail/root ... shebang ...
前振りも空しく、普通に実行できてしまいましたね。環境変数を確認してみましょう。
Bash# crontab -ls */1 * * * * env You have mail in /var/spool/mail/root ... PATH=/usr/bin:/bin ...
そりゃ問題なく実行できますよね。正しくPathが通っていますもの。。。職場でこの問題にはまったのですが、よく考えてみたら自分たちでインストールしたRubyのPathが、変なところにあってenvコマンドが使えないだけでした。。。 とりとめもなく書きすぎてしまいましたがいつの日か清書します。
いつかやる宿題
- 「program loader」はどうやって動いているのか
- 「Interpreter directive」とはどういう意味か