githubに置いてあるスクリプトをcloneせずにcurlで取得するのはインストール手法としてよく見かけます。
curl https://raw.githubusercontent.com/Shougo/dein.vim/master/bin/installer.sh > installer.sh
# For example, we just use `~/.cache/dein` as installation directory
sh ./installer.sh ~/.cache/dein
curl -fsSL https://github.com/rbenv/rbenv-installer/raw/master/bin/rbenv-doctor | bash
rbenvの例のようにパイプで繋げる等して1行で実行まで行えば、実行後のスクリプトの削除なども行わなくていいので便利です。
自分のリポジトリのファイルを使う場合、以前はdein.vimの例のようにダウンロードと実行を分けていたのですが、1行で実行まで行おうとして問題が発生しました。
シェバンで設定したオプションが効かない
実行するスクリプトファイルのシェバンには-eオプションを設定してありファイルを直接実行する際には問題がありませんでした。
#!/bin/bash -e
function error()
{
return 1
}
error
echo "success"
bash-3.2$ ./shebang.sh
bash-3.2$
これをファイルの中身をbash本体に与えて実行するとerrorの箇所をすり抜けてしまいます。
bash-3.2$ bash <(cat ./shebang.sh)
success
bash-3.2$ bash -c "$(cat ./shebang.sh)"
success
bash-3.2$ bash ./shebang.sh
success
これを-eオプションをシェバンではなく、setで与えるように変更するとerrorの箇所で止まります。
#!/bin/bash
set -e
function error()
{
return 1
}
error
echo "success"
bash-3.2$ ./set.sh
bash-3.2$ bash -c "$(cat ./set.sh)"
bash-3.2$ bash <(cat ./set.sh)
bash-3.2$ bash ./set.sh
bash-3.2$
実行の仕組み
シェバンの解釈はexecveシステムコールによって行われるようです。
スクリプトが実行される際の動作はmanによると以下のようになっています。
インタープリタースクリプト
インタープリタースクリプトとは、実行許可が有効になっていて、 最初の行が以下の形になっているテキストファイルのことである。
#! interpreter [optional-arg]
interpreter は有効な実行ファイルのパス名でなければならず、 それ自身がスクリプトであってはならない。 execve() の filename 引き数がインタープリタースクリプトを指定している場合、 interpreter は以下の引き数で起動される。interpreter [optional-arg] filename arg...
つまり
./shebang.sh
を実行した場合
/bin/bash -e ./seban.sh
に置き換えられます。
bash <(cat ./shebang.sh)
このように実行した場合はexecveによるシェバンの解釈が行われないため、先頭行は単なるコメントとして処理されてしまうわけです。
- ターミナル上でコピペしてみた
bash-3.2$ #!/bin/bash -e
bash-3.2$
bash-3.2$ function error()
> {
> return 1
> }
bash-3.2$
bash-3.2$ error
bash-3.2$
bash-3.2$ echo "success"
success
bash-3.2$
bash-3.2$ #!/bin/bash
bash-3.2$ set -e
bash-3.2$ function error()
> {
> return 1
> }
bash-3.2$
bash-3.2$ error
[プロセスが完了しました]
シェバンの制限
シェバンの引数は環境によっては一つしか設定できません。
インタープリタースクリプトの optional-arg 引き数の解釈方法は実装により異なる。 Linux では、インタープリター名 interpreter に続く文字列全体がインタープリターに 1個の引き数として渡される。 しかし、動作が異なるシステムもある。 あるシステムでは、 optional-arg のうち最初のホワイトスペースまでが 引き数として渡される。 また、別のシステムでは インタープリタースクリプトは複数の引き数を持つことができ、 optional-arg 内のホワイトスペースが引き数の区切りとなる。
そのような環境では-と+やロングオプションを同時に設定したい場合などには結局setを使うことになるので、それならばシェバンの引数は指定せず、常にsetを使用するようにしておけばファイルを実行する場合もシェルに読ませる場合も問題がなくなります。