LoginSignup
25

More than 5 years have passed since last update.

Bashの変数スコープについてのメモ

Posted at

要旨

  • 変数は基本的にグローバル変数である。同じプロセス内で参照・変更ができる。
  • サブシェル実行した場合、元プロセスのグローバル変数を参照・変更できる。ただし、値の変更は元プロセスに伝播しない。
  • スクリプト内でsh実行した場合、exportされた元プロセスのグローバル変数のみ参照・変更できる。ただし、値の変更は元プロセスに伝播しない。
  • 関数内においてlocal宣言された変数はその関数内でのみ有効で、関数外には持ち出されない。

検証

環境

  • Ubuntu 18.04 (WSL)
  • bash 4.4.19

bashの実行プロセスについて

本題に入る前に子シェルや関数の実行プロセスについて確認する。

関数の実行

main1.sh
#!/bin/bash

echo "ベタ書き"
echo "main PID/BASHPID/PPID=$$/$BASHPID/$PPID"

func1(){
        echo "func1 PID/BASHPID/PPID=$$/$BASHPID/$PPID"
}

echo "関数呼び出し"
func1

echo "関数呼び出し(サブシェル)"
(func1)

echo "パイプ実行"
func1 >&2 | func1

echo "連続実行"
func1 && func1

実行結果

$ ./main1.sh
ベタ書き
main PID/BASHPID/PPID=376/376/4
関数呼び出し
func1 PID/BASHPID/PPID=376/376/4
関数呼び出し(サブシェル)
func1 PID/BASHPID/PPID=376/377/4
パイプ実行
func1 PID/BASHPID/PPID=376/378/4
func1 PID/BASHPID/PPID=376/379/4
連続実行
func1 PID/BASHPID/PPID=376/376/4
func1 PID/BASHPID/PPID=376/376/4

PID, PPIDは全パターンで同じであるが、サブシェル実行とパイプラインを使用して実行した場合はBASHPIDが変わっている。

スクリプト内でのbash実行

main2.sh
#!/bin/bash

echo "ベタ書き"
echo "main PID/BASHPID/PPID=$$/$BASHPID/$PPID"

echo "bash実行"
./sub.sh
sub.sh
#!/bin/bash

echo "sub PID/BASHPID/PPID=$$/$BASHPID/$PPID"

sfunc(){
        echo "sfunc PID/BASHPID/PPID=$$/$BASHPID/$PPID"
}

sfunc

実行結果

$ ./main2.sh
ベタ書き
main PID/BASHPID/PPID=234/234/4
bash実行
sub PID/BASHPID/PPID=235/235/234
sfunc PID/BASHPID/PPID=235/235/234

sub.shには新しいPIDが発行され、PPIDにはmain2.shのPIDが格納されている。

source実行

main3.sh
#!/bin/bash

echo "ベタ書き"
echo "main PID/BASHPID/PPID=$$/$BASHPID/$PPID"

echo "source実行"
. inc.sh

echo "関数呼び出し"
ifunc
inc.sh
#!/bin/bash

echo "inc PID/BASHPID/PPID=$$/$BASHPID/$PPID"

ifunc(){
        echo "ifunc PID/BASHPID/PPID=$$/$BASHPID/$PPID"
}

実行結果

$ ./main3.sh
ベタ書き
main PID/BASHPID/PPID=244/244/4
source実行
inc PID/BASHPID/PPID=244/244/4
関数呼び出し
ifunc PID/BASHPID/PPID=244/244/4

すべての値が同じになっている。つまり、inc.shの内容をmain3.shに直接書いた場合と同じである。

変数の有効範囲について

同PIDの場合

main1.sh
#!/bin/bash

var_m="M"
. inc2.sh
echo "初期状態"
echo "main var_m/var_i=${var_m}/${var_i}"

func1(){
        var_m="${var_m}m"
        var_i="${var_i}m"
        echo "func1 var_m/var_i=${var_m}/${var_i}"
}

echo "関数呼び出し"
func1
echo "main var_m/var_i=${var_m}/${var_i}"

echo "関数呼び出し(サブシェル)"
(func1)
echo "main var_m/var_i=${var_m}/${var_i}"

echo "inc内関数呼び出し"
ifunc
echo "main var_m/var_i=${var_m}/${var_i}"
inc.sh
#!/bin/bash

var_i="I"

ifunc(){
        var_m="${var_m}i"
        var_i="${var_i}i"
        echo "func1 var_m/var_i=${var_m}/${var_i}"
}

実行結果

$ ./main1.sh
初期状態
main var_m/var_i=M/I
関数呼び出し
func1 var_m/var_i=Mm/Im
main var_m/var_i=Mm/Im
関数呼び出し(サブシェル)
func1 var_m/var_i=Mmm/Imm
main var_m/var_i=Mm/Im
inc内関数呼び出し
func1 var_m/var_i=Mmi/Imi
main var_m/var_i=Mmi/Imi

同じBASHPIDで動いている箇所では読み書きができる。これはsourceで取り込んだ変数・関数においても同様である。
サブシェルでは変数の参照はできるが、変更は元シェルには伝播しない。

スクリプト内でbash実行

main2.sh
#!/bin/bash

var_m="M"
var_e="E"
export var_e

echo "初期状態"
echo "main var_m/var_e=${var_m}/${var_e}"

echo "bash実行"
./sub.sh
echo "main var_m/var_e=${var_m}/${var_e}"
sub.sh
#!/bin/bash

echo "sub var_m/var_e=${var_m}/${var_e}"

sfunc(){
        var_m="${var_m}s"
        var_e="${var_e}s"
        echo "sfunc var_m/var_e=${var_m}/${var_e}"
}

sfunc

実行結果

$ ./main2.sh
初期状態
main var_m/var_e=M/E
bash実行
sub var_m/var_e=/E
sfunc var_m/var_e=s/Es
main var_m/var_e=M/E

exportした変数のみ子シェルからも参照することができる。ただし変更は元シェルには伝播しない。

関数内での宣言

main3.sh
#!/bin/bash

func1(){
        var_g="G"
        local var_l="L"
        echo "func1 var_g/var_l=${var_g}/${var_l}"
}

func1
echo "main var_g/var_l=${var_g}/${var_l}"

実行結果

$ ./main3.sh
func1 var_g/var_l=G/L
main var_g/var_l=G/

関数内が初出の変数もプロセス全体で使用できる。ただしlocalをつけて宣言した場合は関数のみで有効になる。

(おまけ)定数の宣言

readonlyをつけて宣言および代入をする。この変数には再代入ができなくなる。

main4.sh
#!/bin/bash

readonly var_r="R"
var_r="${var_r}r"
echo "main var_r=${var_r}"

実行結果

$ ./main4.sh
./main4.sh: line 4: var_r: readonly variable
main var_r=R

参考サイト

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
25