node.jsおよび、npmの環境構築の際にシェルスクリプトにコマンドをまとめている時に困ったので解決法メモ
シェルスクリプトで環境構築の流れを記述している際に、シェルを再起動する必要がありました。そこで、「exec $SHELL -l」または「exec $SHELL --login」というコマンドをシェルスクリプト内に含めると、そこで処理が終了し、それ以降の処理が続行されなくなり困りました。
事象:シェルスクリプト処理が途中で終了する
「exec $SHELL -l」は、シェルを再起動させるためのコマンドとして使われています。
シェルを再起動して置き換えるこれらのコマンドで、以下のようなスクリプト処理中に処理が途中で止まる問題が発生します。
script.sh:
#bin/sh
echo こんにちは
exec $SHELL -l
echo こんばんは
このスクリプトを「./script.sh」または、「source script.sh」のように実行すると、以下のような出力になります。
ターミナル出力:
こんにちは
ここでは、「こんばんは」まで出力されることを期待していましたが、「exec $SHELL -l」の前の処理までしか実行されませんでした。
原因
シェルスクリプトは基本的に別のシェルを起動して実行されています。明示的に「source script.sh」のように実行すれば、現在のシェルで実行されます。
「exec $SHELL -l」や「exec $SHELL --login」というコマンドは、現在のシェルを新しいログインシェルに置き換えるコマンドらしく、現在のシェルが終了して、新しいシェルを起動させるといった処理を行います。
参考:
ここで、シェルの切り替えが起こった時点で、シェルスクリプトの実行は続行されるという処理にはならず、終了してしまうようです。
解決方法:スクリプト内で再実行する
もっと良い解決法があるような気がしますが、以下のようにif文を用いて処理を前後に分割することで、シェルが再起動しても処理を続行して実行することができます。
script.sh:
#bin/sh
if [ $# -eq 0 ]; then
# 前半処理部 ▼
echo こんにちは
exec $SHELL -l script.sh hoge
else
# 後半処理部 ▼
echo こんばんは
fi
4行目のように「exec $SHELL -l」の末尾に「script.sh {引数}」のように、もう一度、今実行しているシェルスクリプトを再帰的に呼び出します。
このとき、引数を「hoge」のように何かしらか取るなどして、一度目の引数なしの実行「script.sh」で、引数の数が0([ $# -eq 0 ])という条件を満たすことで、処理の前半(if文内)部を処理し、再帰的に呼び出された引数付きの「script.sh hoge」で、引数の個数が0個という条件に当てはまらなくなったため、後半(else文内)部が実行されることで、処理が途中で途切れることなく、実行されます。
ターミナル出力:
こんにちは
こんばんは
出力は期待していたものとなりました。
補足:別スクリプトに分割
以下のように、スクリプトA内で別のスクリプトBを呼び出せば、上のように、条件分岐する必要が無いため、コードの可読性の面では、いいような気がします。
script_a.sh:
exec $SHELL -l script_b.sh
ただ、管理するファイルの数は増えます。
以上、おつかれさまでした。