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

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

シェルスクリプトの作業中に、Bashの特殊変数 $$$BASHPID の間に大きな動作の違いがあることに気づきました。Linux上で実行されるすべてのプロセスにはプロセスID(PID)が割り当てられ、OSはこのIDをもとにプロセスを管理しています。

関連記事: Bashにおけるソース読み込み(source)とフォーク(fork)の違いを学ぶ

同様に、あなたが開いているBashターミナルのセッションにもプロセスIDが割り当てられます。そして、現在のシェルのプロセスIDを格納する特別な変数として "$$""$BASHPID" の2つが用意されています。

現在のシェルのPIDを確認する

まずは以下のコマンドを実行して、現在のシェルのプロセスIDを確認してみましょう。"$$""$BASHPID" はどちらも同じ値を返します。

$ echo $$               # 特殊変数 $$ を表示
$ echo $BASHPID         # 変数 $BASHPID を表示
Bashの特殊変数「$$」と「$BASHPID」の違いを徹底解説

サブシェル(子プロセス)の仕組み

Bashでは、シェルから外部プログラムを呼び出すと子プロセス(サブシェル)が作成され、そのプログラムは子プロセス内でのみ実行されます。以下の例では、親シェルがどのようにサブシェルを作成してプログラムを実行するかを示すため、簡単なプロセス監視コマンドを sample.sh というスクリプトに記述しました。

#!/usr/bin/env bash

ps -ef --forest | grep -i bash

このスクリプトを実行すると、bashのプロセスIDを確認できます。下の画像を見ると、スクリプトを呼び出した際にbashが子プロセスを作成し、その中でスクリプトを実行していることがわかります。

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

スクリプト内で両方の変数を出力する

次に、"$$""$BASHPID" をスクリプト内で使って、それぞれ何を返すのか確認してみましょう。

#!/usr/bin/env bash
echo "============================"
ps -ef --forest | grep -i bash
echo "============================"
echo "PID USING $ FOR SCRIPT $0 ==> $$"
echo "PID USING BASHPID FOR SCRIPT $0 ==> $BASHPID"
echo

スクリプトを再度実行します。

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

結果はどちらも同じプロセスIDを返しました。ここからが本当の違いです。続いて、スクリプト内でコマンドを 丸括弧() の中で実行し、新たな子プロセスを作成してみます。

# PIDを変数に格納…

VAR_HASH=$(echo $$)
VAR_BASHPID=$(echo $BASHPID)

echo "VALUE OF VAR_HASH ==> $VAR_HASH"
echo "VALUE OF VAR_BASHPID ==> $VAR_BASHPID"
Bashの特殊変数「$$」と「$BASHPID」の違いを徹底解説

Bashでは、丸括弧 を使うと子プロセスが起動され、括弧内のコマンドがその子プロセス上で実行されます。この場合、$$$BASHPID の両方が新しい子プロセスのIDを保持すると予想するかもしれません。しかし、上の画像を見ると明確な違いがあります。$$382 という値、つまり親プロセスのID(スクリプト sample.sh のプロセスID)を格納しているのに対し、$BASHPID は丸括弧によって新しく作成された子プロセスのIDを格納しています。

manページで公式の挙動を確認する

それでは、この挙動について理解を深めるために、manページには何と書かれているのか見てみましょう。

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

$$ を使用した場合、サブシェル内であっても、そのサブシェルが生成された元である親プロセスのプロセスIDを格納します。一方、BASHPID は現在のプロセスIDを格納します。つまり、丸括弧の中で呼び出された場合には、子プロセスのIDが格納されるのです。

変数への代入とunsetの挙動

$$ は代入や変更が一切できませんが、BASHPID は再代入自体は可能です。ただし、値に反映されることはありません。

$ $=10
$ BASHPID=10
$ echo $BASHPID
Bashの特殊変数「$$」と「$BASHPID」の違いを徹底解説

さらに興味深いことに、BASHPID はunset(変数の削除)することもできます。unsetすると特殊変数としての状態を失い、以降は通常の変数として自由に使えるようになります。

$ unset BASHPID
$ echo $BASHPID
$ BASHPID="Tecmint"
$ echo $BASHPID
Bashの特殊変数「$$」と「$BASHPID」の違いを徹底解説

unset後であれば、シェル自身のプロセスIDを代入しても、それは単なるユーザー定義変数として扱われます。すでに特殊な状態を失っているためです。

$ BASHPID=$(echo $$)
$ echo $$;echo $BASHPID
Bashの特殊変数「$$」と「$BASHPID」の違いを徹底解説

この場合、BASHPID の特殊な状態を取り戻すには、新しいターミナルセッションを開き直す必要があります。

まとめ

本記事では、$$BASHPID の違いと、それぞれの挙動について解説しました。ポイントをおさらいすると、以下の通りです。

  • $$:常に元のシェル(親シェル)のPIDを返す。サブシェル内でも値は変わらない。代入・変更は不可。
  • $BASHPID:現在実行中のbashプロセスのPIDを返す。サブシェル内では新しいPIDとなる。再代入は可能だが効果なし。unsetすると特殊変数としての性質を失う。

ぜひ本記事を参考に、ご自身の環境でも実際に試してみてください。フィードバックがあればお気軽にお寄せください。


  1. PythonとBashの違いとは?7つの観点で比較して徹底解説

    PythonとはPythonは、シンプルな実装と高い可読性を重視して設計されたプログラミング言語です。動的型付けを採用しており、変数の型を事前に宣言する必要がありません。また、C言語のようなポインタという概念を持たないため、初心者でも直感的にコードを書きやすい点が大きな特徴です。BashとはBash(Bourne Again Shell)は、コマンドラインインタプリタの一種で、LinuxやmacOSには標準で搭載されています。その他のOSにもインストールして利用することが可能です。LinuxおよびmacOSにおいては、デフォルトのユーザーシェルとして広く使われています。以下に、PythonとB

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

    この記事では、Bashでスクリプトを実行する場合とsourceコマンドで読み込む場合の違いについて、内部で何が起こっているのかを詳しく解説します。まずは、スクリプトの呼び出し方によってプログラムがどのように処理されるのかを理解していきましょう。 補足: スクリプトに拡張子を付けるかどうかは動作に影響しません。拡張子がなくても問題なく実行できます。 シェバン(#!)とは 基本的に、すべてのスクリプトはシェバン(#!)と呼ばれる行から始まります。Bashでは#(ハッシュ)記号はコメントとして解釈されますが、シェバンには特別な意味があります。シェバンは、指定されたインタプリタにプログラムの実行を委ね