Chienomi

Shabangとenvの関係

Live With Linux::newbie

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を使うと、きっと心に余裕ができることだろう。