linuxBean14.04(114)「./myscript」「myscript」「source myscript」「. myscript」の違い

以前もどこかでやったような気もしますがどこでやったかわからなくなったので整理しておきます。linux - What is the difference between executing a bash script and sourcing a bash script? - Super Userを参考にしました。(2016.5.29追記。linuxBeanのTerminalはdashと勘違いしていたので書き直しました。)

前の関連記事:linuxBean14.04(113)SyntaxHighlighter v4のビルド


そのシェルはdashかbashか?


dashかbashはDashAsBinSh - Ubuntu Wikiに詳しい解説があります。

dashかbashかで挙動が変わってくるのでまずどちらを使っているかを知っておく必要があります。

bashであれば~をホームディレクトリのパスに置換してくれますがdashはしてくれません。

bashであればsourceも.も同じ働きをしますがdashではsourceは使えず.しか使えません。

echo $SHELL

bashであればこれで/bin/bash、dashであれば/bin/dashか/bin/shが表示されると思ったのですがdashと思われる状況でも/bin/bashがでてきました。

linuxBeanのTerminalは~も展開されますしsourceも使えるのでbashだと思います。
pq@pq-VirtualBox:~$ echo $SHELL
/bin/bash
bashでなくdashになっているためにsourceが使えない例を作ってみましょう。

まずbashで動く例を作ります
#!/bin/bash
echo "secondary_scriptの\$SHELL" $SHELL
secondary_script.shという名前でこれを保存して実行権限をすべてにします。
pq@HP6730b1:~/_tmp$ ./secondary_script.sh
secondary_scriptの$SHELL /bin/bash
secondary_script.shのshebangがbashなので当然の結果だと思います。

ところがshebangを/bin/shに変更しても同じように/bin/bashが返ってきます。

ということは/bin/shはbash?、と考えたくなります。

今度はこのsecondary_script.shを呼び出すprimary_script.shを作成します。
#!/bin/bash
echo "primary_scriptの\$SHELL" $SHELL
echo sourceでsecondary_script.shを実行した結果
source ./secondary_script.sh
echo .でsecondary_script.shを実行した結果
. ./secondary_script.sh
primary_script.shのパーミッションの実行をすべてにして保存します。

shebangがbashになっているのでbashで起動します。
pq@HP6730b1:~/_tmp$ ./primary_script.sh
primary_scriptの$SHELL /bin/bash
sourceでsecondary_script.shを実行した結果
secondary_scriptの$SHELL /bin/bash
.でsecondary_script.shを実行した結果
secondary_scriptの$SHELL /bin/bash
これはすべて問題なく実行されました。

今度はこのprimary_script.shのshebangを/bin/shに変更して実行します。
pq@HP6730b1:~/_tmp$ ./primary_script.sh
primary_scriptの$SHELL /bin/bash
sourceでsecondary_script.shを実行した結果
./primary_script.sh: 4: ./primary_script.sh: source: not found
.でsecondary_script.shを実行した結果
secondary_scriptの$SHELL /bin/bash
sourceコマンドがないと言われるのでbashでなくdashが実行されていることがわかります。

だけど$SHELLは/bin/bashが入っています。

いろいろやってみましたがecho $SHELLで/bin/dashか/bin/shかを表示させるようにはできませんでした。

とりあえず$SHELLではbashかdashか区別できないことがわかりました。

shebangをshかdashにした時はsourceではなく.を使わないといけません。

sourceが使えるかどうかでbashかdashの判断をすることになります。

さらに気がついたbashとdashの違いは、実行権限を付与したスクリプトでもdashではカレントディレクトリからのパスを付けないと実行できませんでした。
#!/bin/bash
echo "primary_scriptの\$SHELL" $SHELL
echo sourceでsecondary_script.shを実行した結果
source secondary_script.sh
echo .でsecondary_script.shを実行した結果
. secondary_script.sh
これは動きますがshebangを/bin/shにするとsecondary_script.shがないと言われてエラーになります。

以上を踏まえて「./myscript」「myscript」「source myscript」「. myscript」の違いを考えます。

スクリプトのあるパスとパーミッションによって実行できる方法が変わる


linuxBeanのTerminalはbashなので「source myscript」と「. myscript」は同じ


bashでは「.」は「source」のエイリアスです。

しかしdashでは「source」は使えません。([Ubuntu][Shell]Ubuntuの/bin/shでsourseコマンドが効かない件 | aoshiman.org)。

linuxBeanのTerminalはbashなので「. myscript」は「source myscript」と全く同一のことをしていることになります。

ただし/bin/shは/bin/bashではなく/bin/dashのシンボリックなのでシェルスクリプトのshebangを書くときは注意が必要です。

スクリプトのあるパスが環境変数PATHにあり、かつ、スクリプトの実行権限があるとき

bashでもdashでも「myscript」で実行できます。

「myscript」で実行できるときはbashであれば「source myscript」「. myscript」、dashであれば「. myscript」でも実行できます。

スクリプトのあるパスが環境変数PATHにあるがスクリプトの実行権限がないとき

bashであれば「source myscript」「. myscript」で実行できます。

dashであれば「. myscript」で実行できます。

スクリプトがカレントディレクトリにあるとき

「./myscript」で実行できます。

「./myscript」で実行できるときはbashであれば「source myscript」「. myscript」でも実行できます。

dashの場合は「source ./myscript」「. ./myscript」とする必要があります。

$0を考慮しなくてよいときは「. ./myscript」の汎用性が高い


上記の結果を踏まえるとlinuxBeanのTerminalでは「. myscript」が一番汎用性が高いです。

dashも考慮すると「. ./myscript」がさらに汎用性が高くなります。

「. ./myscript」であれば環境変数PATHにあれば実行権限がなくてもどのディレクトリでも実行できます。

「. ./myscript」であれば環境変数PATHになくてもカレントディレクトリにあれば実行できます。

さらにsourceコマンドを使うことになるので現在のシェルの環境変数も設定できます。(シェル変数と環境変数の違いをコマンドラインで確認する - Qiita)

しかしスクリプト自信を指す$0を処理するシェルスクリプトでは. ./myscriptでは問題が起こります。

「. ./myscript」で全てが実行可能というわけではない


全部「. ./myscript」でやろうと思ったら、NetBean8.1のインストールスクリプトはエラーがでてインストール出来ませんでした。

「sh myscript」だとうまくいきます。

これは引数を受け取る変数の問題と気が付きました。
#!/bin/sh
echo "引数\$0 $0"
echo "引数\$1 $1"
echo "引数\$2 $2"
このようなスクリプトargs_test.shを作ってパーミッションの実行を「すべて」に変更します。

これに引数を2つつけて上記の方法をすべて実行してみます。
pq@pq-VirtualBox:~/_tmp$ export PATH=.:$PATH
pq@pq-VirtualBox:~/_tmp$ source args_test.sh arg1 arg2
引数$0 bash
引数$1 arg1
引数$2 arg2
pq@pq-VirtualBox:~/_tmp$ . args_test.sh arg1 arg2
引数$0 bash
引数$1 arg1
引数$2 arg2
pq@pq-VirtualBox:~/_tmp$ ./args_test.sh arg1 arg2
引数$0 ./args_test.sh
引数$1 arg1
引数$2 arg2
pq@pq-VirtualBox:~/_tmp$ args_test.sh arg1 arg2
引数$0 ./args_test.sh
引数$1 arg1
引数$2 arg2
pq@pq-VirtualBox:~/_tmp$ sh args_test.sh arg1 arg2
引数$0 args_test.sh
引数$1 arg1
引数$2 arg2
pq@pq-VirtualBox:~/_tmp$ bash args_test.sh  arg1 arg2
引数$0 args_test.sh
引数$1 arg1
引数$2 arg2
sourceや.でスクリプトを実行した場合はキーボードから直接コマンドを打ち込んだのと同じ扱いになるため$0にシェルスクリプト名が反映されなくなります。

なので$0を使っているシェルスクリプトはsourceや.を使うと結果が他の方法と異なる可能性があります。

結論としてはbash myscriptが一番汎用性がありそうです。

参考にしたサイト


linux - What is the difference between executing a bash script and sourcing a bash script? - Super User
「./myscript」「myscript」「source myscript」「. myscript」の違いについて例も挙げて解説されています。

DashAsBinSh - Ubuntu Wiki
dashとbashの違いの詳しい解説。

[Ubuntu][Shell]Ubuntuの/bin/shでsourseコマンドが効かない件 | aoshiman.org
dashではsourceが使えず代わりに.を使います。

シェル変数と環境変数の違いをコマンドラインで確認する - Qiita
環境変数の引き継ぎについてわかりやすく解説されています。

次の関連記事:linuxBean14.04(115)pythonコマンドをpipコマンドとともに切り替える

PR

0 件のコメント:

コメントを投稿