↓ メインコンテンツへスキップ

source コマンドの使い方と親子プロセス

·2095 文字·5 分
わて
著者
わて

仕事でよくshell でプログラムを組むのですが、その中でsourceコマンドを使う機会があったので備忘録としてまとめたいと思います。

筆者は最近Linux カーネルについて改めて勉強が必要だなとも思っていたので、今回の勉強は良い機会でした。

この記事ではsourceコマンドとプロセスの親子関係について説明します。
参考にする際は、ぜひ実際に手を動かしながらやってみてください。WSLでも動く内容です(動かなかったらごめん)。

先にまとめ
#

この記事では、以下の内容について実際に手を動かしながら理解できるように説明します。

  • 親プロセスと子プロセスの関係
  • source コマンドは子プロセスを作らず外部ファイルを読み込める
  • shell スクリプトでの実用

親プロセスと子プロセス
#

そもそもプロセスとは、簡単に言うと、動作中のプログラムのことです。
その動作しているプログラムは、別のプログラムを起動させることがあります。

shell で書くとこんな感じです↓

parent.sh
#!/bin/bash

./child.sh # ← 呼び出してる

いま、parent.shはchild.shを呼び出しています。
このとき、parent.shはchild.shの親プロセスであり、child.shは子プロセスです。

実際にやってみましょう。
子プロセスを生成するような仕組みを作り、そのPID(プロセスID)を確認してみます。

parent.shとchild.shの二つを用意して以下のように記載しておきます。また、実行権限も渡しておきます。

parent.sh
#!/bin/bash

echo "parent process: $$"
./child.sh
child.sh
#!/bin/bash

echo "child process: $$"

$$とは特殊な文字で、自身のPID を出力します。
parent.shではchild.shを呼び出しているので、child.shは子プロセスとなり、PID も異なるはずです。

実際にparent.shを実行してみるとPID は親と子で異なることがわかります。

# $ ./parent.sh
parent process: 14774
child process: 14775

source コマンドについて
#

上の実験では子プロセスとしてchild.shを呼び出しました。視覚的にプロセスの親子関係が確認できたと思います。

後述するように外部ファイルを動作させたいが、子プロセスとして動作させたくないことがあります。

そんなときに使えるのがsourceコマンドです。

先程のparent.shを以下のように編集し、child.shをsource コマンドを使って読み込みます。そうすると同じPID が出力されました。

parent.sh
#!/bin/bash

echo "parent process: $$"
source ./child.sh
# $ ./parent.sh
parent process: 15159
child process: 15159

このように、sourceコマンドでは子プロセスは生成されません。

厳密には、child.shに記載されている文字がparent.shにより読み込まれたに等しいです。要するに、新しいプロセスは生成されておらず記述だけが外にある感じです。

shell スクリプトでの実用
#

子プロセスを作る作らないで何が変わるんだよと思いますが、これはexport を用いるときや外部ファイルの関数を呼び出すときに便利です。

exportを用いる例
#

スクリプト内で環境変数を使うときに、プロセスの親子関係を気にする必要があります。

子プロセスでなされたexportは子プロセスが終了すると環境変数も消えてしまいます。なので、環境変数を定義するスクリプトを別ファイルに置くときはsourceコマンドで呼び出す必要があります。

ちょっと実験してみましょう。
parent_export.shとchild_export.shという二つのファイルを用意してください。

今回も動作させるのは親プロセスになるもののみなのですが、child_export.shをあえて子プロセスとして呼び出します。
子プロセスについてはgreetingという環境変数を定義するのみです。

parent_export.sh
#!/bin/bash

./child_export.sh

echo $greeting
child_export.sh
#!/bin/bash

export greeting="Hello"

この状態でparent_export.shを実行してもなにも何も出力されません。

しかし、sourceコマンドを使ってみると、

parent_export.sh
#!/bin/bash

source ./child_export.sh

echo $greeting

以下のようにchild_export.shで定義した文字が親プロセスで扱えることがわかります。

# $ ./parent_export.sh
Hello

筆者は仕事でコンピューターの製造をしてるのですが、動作確認として負荷テストをかけます。そのとき、マシンのCPU のアーキテクチャやOS によって必要な環境変数が異なるので上記のようにsourceコマンドを仕様して動的な環境変数の設定をさせています。

外部ファイルの関数呼び出し
#

また、外部ファイルに書いてある関数を呼び出したいときにもsourceコマンドは活躍します。

これは見たほうが早いのでmain.shとfunctions.shの二つのファイルを用意ください。

main.sh
#!/bin/bash

source functions.sh
funcA
funcB
functions.sh
#!/bin/bash

function funcA(){
        echo "this is A"
}
function funcB(){
        echo "this is B"
}

この状態でmain.shを叩いてみます。

# ./main.sh
this is A
this is B

そうすると、functions.shで定義された関数が正しくmain.shで動いていることがわかります。

なぜこれで読み込める?

上でも説明したとおり、sourceコマンドは「記述だけ外にある」を実現できます。なので、上の場合は以下のような代入がなされます。

main.sh
#!/bin/bash

# source functions.sh この行が以下に置き換わる
# -----
function funcA(){
        echo "this is A"
}
function funcB(){
        echo "this is B"
}
# -----
funcA
funcB

なので当然のようにfuncAもfuncBも呼び出すことができます。

まとめ
#

まとめについては先にまとめの通りです。