LoginSignup
2
1

More than 3 years have passed since last update.

シェバンにはオプションを設定せずsetを使う方が良さそう

Posted at

githubに置いてあるスクリプトをcloneせずにcurlで取得するのはインストール手法としてよく見かけます。

dein.vim
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
rbenv
curl -fsSL https://github.com/rbenv/rbenv-installer/raw/master/bin/rbenv-doctor | bash

rbenvの例のようにパイプで繋げる等して1行で実行まで行えば、実行後のスクリプトの削除なども行わなくていいので便利です。

自分のリポジトリのファイルを使う場合、以前はdein.vimの例のようにダウンロードと実行を分けていたのですが、1行で実行まで行おうとして問題が発生しました。

シェバンで設定したオプションが効かない

実行するスクリプトファイルのシェバンには-eオプションを設定してありファイルを直接実行する際には問題がありませんでした。

shebang.sh
#!/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の箇所で止まります。

set.sh
#!/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$ 
setはOK
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を使用するようにしておけばファイルを実行する場合もシェルに読ませる場合も問題がなくなります。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1