sh script.sh , bash script.sh , source script.sh の違いと bash , dash の違いについて解説します。
sh
sh は基本的に Symbolic Link になっており、リンク先のコマンドが実行されます。
RedHat 系の場合はリンク先が bash に指定されています。以下は Rocky Linux 8.6 の例です。which sh では "sh" コマンドのフルパスを確認し、ls -l でシンボリックリンクのリンク先を確認しています。
[test@rocky1 ~]$ which sh /usr/bin/sh [test@rocky1 ~]$ ls -l /usr/bin/sh lrwxrwxrwx. 1 root root 4 4月 12 16:05 /usr/bin/sh -> bash
一方、ubuntu はデフォルトのログインシェルは bash なのですが、sh のシンボリックリンク先は dash になっています。以下は Ubuntu 20.04.4 LTS の例です。echo $0 でログイン直後のシェルを確認し、which sh で場所確認、ls -l でリンク先を確認しています。
test@ubuntu1:~$ echo $0 -bash test@ubuntu1:~$ which sh /usr/bin/sh test@ubuntu1:~$ ls -l /usr/bin/sh lrwxrwxrwx 1 root root 4 Feb 23 08:50 /usr/bin/sh -> dash
なので sh script.sh を動作させたときは基本的に RedHat 系なら bash, Ubuntu なら dash でそのスクリプトが動作します。
bash
bash script.sh を動作させたときは RedHat 系でも Ubuntu でも bash でそのスクリプトが動作します。
つまり RedHat 系であれば sh script.sh と bash script.sh は同義です。
ちなみに bash を使わずとも直接コマンドを実行しても同じです。つまり、bash script.sh でも ./script.sh でも /home/test/script.sh でも同じ実行結果になります。
source
この source コマンドは bash に内包 (ビルトイン) されているコマンドですので dash 上では使えません。つまり source script.sh が動作したのならそれは bash でそのスクリプトが動作しています。
では bash と source の違いは何かというと、bash script.sh (あるいは ./script.sh) は現在の bash プロセスを複製 (clone) し別プロセスの bash で script.sh を実行するのに対し、source script.sh は現在の bash 上で動作させます。
source は現在のシェルの環境変数を変更するときなど、現在のシェル自身に操作を加えるときに効果を発揮します。最近よく使われるのは python の仮想環境でしょうか。これはまさにシェルの環境を変える操作として行っています。
前述の PATH を追加する具体例を見てみます。
home ディレクトリ /home/test の直下に testbin というディレクトリを作成し、"hello world" を出力する test1.sh というコマンドを配置します。環境変数 PATH には /home/test/testbin は無いので当然コマンドとしては見つかりません。
test@ubuntu1:~$ mkdir testbin test@ubuntu1:~$ echo 'echo "hello world"' > testbin/test1.sh test@ubuntu1:~$ chmod +x testbin/test1.sh test@ubuntu1:~$ test1.sh test1.sh: command not found
次に、環境変数の PATH に /home/test/testbin を追加するスクリプト script.sh を作成します。
test@ubuntu1:~$ cat <<EOF > script.sh
echo before
export PATH="/home/test/testbin:\$PATH"
echo after
EOF
test@ubuntu1:~$ chmod +x script.sh
この状態で bash script.sh を実行した際に今操作している bash には PATH は追加されません。前述の通り、clone したプロセス上で PATH が追加されますが、そのプロセスは実行完了後に消滅するからです。(シェルへの操作ではない before after の出力は実施されています。)
test@ubuntu1:~$ bash script.sh before after test@ubuntu1:~$ echo $PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin test@ubuntu1:~$ test1.sh test1.sh: command not found
ですが、source script.sh を実行すると、そのシェル上で実行されるため、PATH が追加され、test1.sh もフルパス無しで実行できるようになります。
test@ubuntu1:~$ source script.sh before after test@ubuntu1:~$ echo $PATH /home/test/testbin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin test@ubuntu1:~$ test1.sh hello world
今回は script.sh というスクリプトを例に挙げていますが、スクリプトに限らずコマンドも同じく、bash プロセスを複製 (clone) して引き渡されたコマンドを実行 (execve) しています。
なぜ別プロセスなのに bash 上に出力されるか、というと、複製されたプロセスは標準入力/出力のファイルディスクリプターを引き継ぐからです。
現場での Linux 運用においては今回のように呼び出し元のシェルに対して操作することは少ないでしょう。(あったとしても手順化されているような定型業務でしょう。)
基本的には「呼び出し元シェル以外への処理実行と return 値」が得られれば良いケースがほとんどだと思います。その場合は source を使う必要はありません (使っても bash の結果と変わりません)。
なお . (dot + スペース) も source と同義になりますので "source script.sh" の代わりに ". script.sh" を実行しても同じ結果になります。
bash と dash の違い
dash は bash と比べて軽量です。以下の例では bash が 1.2 MB あるのに対し dash は 127 KB になっています。
test@ubuntu1:~$ ls -lh /usr/bin/bash -rwxr-xr-x 1 root root 1.2M Jun 18 2020 /usr/bin/bash test@ubuntu1:~$ ls -lh /usr/bin/dash -rwxr-xr-x 1 root root 127K Jul 18 2019 /usr/bin/dash
dash を使ってみると分かるのですが、タブ補完が効かない、history も無い (ゆえに ↑ キーでの実行履歴表示も不可) など、人間が操作するにはとても不便です。
なので dash は人間が操作することには不向きな分、固定のスクリプトを動作させることについては高速・低負荷で実行できるので向いているでしょう。
まとめ
RedHat 系 | Ubuntu | |
---|---|---|
sh script.sh | 別プロセスで bash が script.sh を実行 | 別プロセスで dash が script.sh を実行 |
bash script.sh ./script.sh | 別プロセスで bash が script.sh を実行 | 別プロセスで bash が script.sh を実行 |
source script.sh . script.sh | 操作中の bash 上で script.sh を実行 | 操作中の bash 上で script.sh を実行 |
コメント