Bashプログラミング
 Computer >> コンピューター >  >> プログラミング >> Bashプログラミング

Bashの「実行」と「source」の違いを徹底解説!シェバン・フォーク・環境変数の仕組み

この記事では、Bashでスクリプトを実行する場合sourceコマンドで読み込む場合の違いについて、内部で何が起こっているのかを詳しく解説します。まずは、スクリプトの呼び出し方によってプログラムがどのように処理されるのかを理解していきましょう。

補足: スクリプトに拡張子を付けるかどうかは動作に影響しません。拡張子がなくても問題なく実行できます。

シェバン(#!)とは

基本的に、すべてのスクリプトはシェバン(#!)と呼ばれる行から始まります。Bashでは#(ハッシュ)記号はコメントとして解釈されますが、シェバンには特別な意味があります。シェバンは、指定されたインタプリタにプログラムの実行を委ねるようBashに指示する役割を持っています。

以下はサンプルプログラムで、bashをインタプリタとして指定しています。

$ cat >> Hello_World.sh
#!/usr/bin/env bash
echo "Hello world"

$ chmod +x Hello_world.sh

スクリプトの実行方法

作成したスクリプトを実行するには、主に2つの方法があります。

  • 相対パスで呼び出す: スクリプトが存在するディレクトリへ移動し、./Hello_world.shのように実行します。
  • 絶対パスで呼び出す: ファイルシステム上のどこからでも、スクリプトへのフルパスを指定して実行します。
$ ./Hello_world.sh
$ pwd
$ /home/karthick/Hello_world
Bashの「実行」と「source」の違いを徹底解説!シェバン・フォーク・環境変数の仕組み

シェバンがない場合はどうなる?

次に、シェバンなしでプログラムを実行しようとした場合に何が起こるのかを見てみましょう。シェバンがない場合、プログラムは現在実行中のシェルにそのまま渡されます。この例ではBash(/bin/bash)です。

具体例を挙げてみます。シェバンなしでPythonスクリプトを作成し、実行するとどうなるでしょうか。BashはこのプログラムをPythonインタプリタに渡すべきだと認識できないため、現在のシェル内でそのまま実行してしまいます。

$ cat > run-py.py
echo $SHELL
print("Hello world")

$ chmod +x run-py.py
$ ./run-py.py
Bashの「実行」と「source」の違いを徹底解説!シェバン・フォーク・環境変数の仕組み

このような場合は、どのインタプリタに処理を渡すかを明示的に指定して呼び出すか、常におすすめされる方法であるシェバン行を追加しましょう。

# which python3
$(which python3) /home/karthick/run_py.py
Bashの「実行」と「source」の違いを徹底解説!シェバン・フォーク・環境変数の仕組み

スクリプト実行時の仕組み:フォーク

スクリプトの呼び出し方がわかったところで、次は実際にスクリプトを呼び出したときに何が起こるのかを理解しましょう。上記の例のようにスクリプトを起動すると、子プロセスが作成され(フォーク)、スクリプトはその子プロセスに渡されて実行されます。

以下のコマンドを実行すると、スクリプトが子プロセスとして実行されていることを確認できます。

$ ps -ef --forest | grep -i bash
Bashの「実行」と「source」の違いを徹底解説!シェバン・フォーク・環境変数の仕組み

1つのスクリプトの中で複数の子プロセスが生成されることもあり、その数はコードの内容によって変わります。ここで重要なポイントとして、サブプロセス内で作成された環境変数は、プロセス終了時に破棄されるという点が挙げられます。子プロセスは、親プロセスがexportした変数を参照できますが、逆に親プロセスから子プロセス内で作成された変数を参照することはできません。

変数の仕組みやexportの方法についてさらに詳しく知りたい方は、以下の記事も参考にしてください。

  • シェルスクリプトにおけるLinux変数の理解と書き方
  • Bashにおける$$と$BASHPIDの違いを学ぶ

sourceコマンドでスクリプトを読み込む(ソーシング)

sourceはシェル組み込みコマンドの一つで、引数として渡されたファイルを読み込み、そのコードを現在のシェル環境で実行します。最もよくある使用例としては、.bashrc.bash_profileの設定を変更した後に、sourceコマンドで変更を再読み込みするケースです。

$ type -a source
Bashの「実行」と「source」の違いを徹底解説!シェバン・フォーク・環境変数の仕組み

sourceコマンドには2つの構文があり、どちらを使うかは個人の好みで選べます。

$ source FILE_NAME [ARGUMENTS]
$ . FILE_NAME [ARGUMENTS]

sourceの動作を実演

それでは、sourceが実際にどのように機能するのかを見てみましょう。2つのシェルスクリプトを作成します。1つ目のスクリプト(Module.sh)は変数と関数を定義し、2つ目のスクリプト(Main.sh)はその変数を出力して関数を呼び出します。

ファイル Module.sh

#!/usr/bin/env bash

VAR1=$(echo "Welcome to $1")

function f1(){
  echo "Function f1 is called"
}

ファイル Main.sh

#!/usr/bin/env bash

echo $VAR1
f1
Bashの「実行」と「source」の違いを徹底解説!シェバン・フォーク・環境変数の仕組み

スクリプトに実行権限を設定し、メインスクリプトmain.shを実行してみます。この状態では、スクリプトは現在のシェル環境内に関数f1や変数VAR1を見つけられず、「command not found」エラーで失敗します。

$ bash main.sh
Bashの「実行」と「source」の違いを徹底解説!シェバン・フォーク・環境変数の仕組み

今度は、スクリプト内でsourceコマンドを使用してみます。これにより、変数と関数が現在のシェル環境に読み込まれ、main.shからアクセスできるようになります。

ファイル Module.sh

#!/usr/bin/env bash

VAR1=$(echo "Welcome to $1")

function f1(){
  echo "Function f1 is called"
}

ファイル Main.sh

#!/usr/bin/env bash

source module.sh Tecmint
echo $VAR1
f1
Bashの「実行」と「source」の違いを徹底解説!シェバン・フォーク・環境変数の仕組み

もう一度スクリプトを実行して、結果を確認してみましょう。

$ bash main.sh

まとめ:モジュール化とDRY原則

sourceは、Bashでシェルスクリプトを作成する際にモジュールプログラミングのアプローチを実現するために非常に便利な機能です。コードを小さなモジュールに分割すれば、多くのプログラムから再利用できるようになります。このようにすることで、DRY(Don't Repeat Yourself)の原則に従った効率的な開発が可能になります。

今回は以上です。この記事では、Bashにおけるソーシングとフォークの違いについて解説しました。ぜひ記事の内容を実際に試しながら、理解を深めてください。

  1. GoとJavaの違いを徹底比較!特徴と使い分けのポイント

    プログラミング言語を選ぶ際、「Go」と「Java」のどちらを採用すべきか迷う開発者は少なくありません。どちらもGoogleや企業システムで広く使われる人気言語ですが、設計思想や言語仕様には大きな違いがあります。本記事では、それぞれの言語の特徴を解説したうえで、両者の主な違いを比較表を使ってわかりやすく整理します。 GoとはGoはGoogleが開発した手続き型(プロシージャル)プログラミング言語です。プログラムはパッケージ単位で構成され、シンプルで読みやすいコード記述を重視した設計になっています。動的言語に近い柔軟なパターンを取り入れつつ、静的型付けによる安全性も兼ね備えています。また、軽量スレ

  2. Bashの特殊変数「$$」と「$BASHPID」の違いを徹底解説

    シェルスクリプトの作業中に、Bashの特殊変数 $$ と $BASHPID の間に大きな動作の違いがあることに気づきました。Linux上で実行されるすべてのプロセスにはプロセスID(PID)が割り当てられ、OSはこのIDをもとにプロセスを管理しています。 関連記事: Bashにおけるソース読み込み(source)とフォーク(fork)の違いを学ぶ 同様に、あなたが開いているBashターミナルのセッションにもプロセスIDが割り当てられます。そして、現在のシェルのプロセスIDを格納する特別な変数として $$ と $BASHPID の2つが用意されています。 現在のシェルのPIDを確認する まずは以