Shabangとenvの関係
Live With Linux::newbie
- TOP
- Articles
- Live With Linux
- Shabangとenvの関係
序
Shebangというのはこういうやつだ。
#!/bin/sh
これはLinuxの場合1、実行ファイルとして実行されたときにカーネルによって解釈される。
これを「おまじない」などと呼ぶ悪しき風習も存在していたりするが、別にそれ自体は難しいものではない。
#!/bin/sh
というshebangを持つa.sh
を./a.sh
の形で実行すると、コマンドが/bin/sh
、第一引数が./a.sh
になる。
一応、これは
#!sh
のように書いても大丈夫なのだが、なぜかDebianでこれをやったら通らなかったので、大丈夫とは言いづらい……
でまぁ、そういう「フルパスを固定したくないがための抽象化」に使われるのがenv(1)だ。 こんな感じ。
#!/bin/env ruby
だが、これもおまじないだと思っている人もいるかもしれない。
env(1)はコマンド実行時に環境変数をセットするためのコマンドである。 現代のシェルであれば
FOOENV=foo env
とすれば分かるように、コマンドの前にname=value
の形式で環境変数つきでコマンドを実行できる。
そしてこれは別に新しい機能ではなく、Bourne Shellにもあった機能だ。
だが、cshにはなかったので、cshでは必要とされた。
env FOOENV=foo env
そして、env(1)はPOSIXに含まれ、「最もあることが期待できるshebangで抽象的にコマンドを実行する方法」となった。
envでいいの?
実はenvを挟むことで、shebangの使用上の問題がある。
まず、shebangは引数を取れるため、
#!/usr/bin/perl -p
s/foo/bar/g
のようにスクリプトをより強力に使ったり、挙動を変えたりといったことが可能だ。
ところが、shebangの解釈は「ワードを2つにスプリットする」であるため、これを
#!/bin/env perl -p
s/foo/bar/g
とすると
/bin/env: 'perl -p': No such file or directory
となってしまう。
抽象化する場合、#!sh
のように書かないのであればenv(1)を使うのが最も無難ではあるのだが、実は問題もある。
対応済みなenv
が、env(1)はそういう使われ方をするものなので、env(1)側でも対応済みである。それが-S
オプションだ。
env(1)に渡せる引数もひとつだけなので、-S
だけを渡すことはできないが、env(1)は渡された引数が-S
オプションを含んでいるっぽい場合は、渡された引数をスプリットする。
なので、
#!/bin/env -S perl -p
s/foo/bar/g
は通るのだ。
envは正しくはないが最善ではある
env
は別にコマンドを抽象化するためのコマンドではない。
なので、コマンドの抽象化のために使うのは正しくはない。
が、その目的でenv(1)が使われてた関係でenv(1)側がそういう使われ方を想定している。 そして、POSIXに含まれているから存在に対する期待値は高い。
このため、「どうするのが一番いいか」ということを考えていくと、env(1)を使うのがいいだろう、ということになる。
ということを知った上でenv
を使うと、きっと心に余裕ができることだろう。