Linuxのbashシェルスクリプト/Windows batスクリプトの別スクリプトを呼んだときのカレントディレクトリと環境変数スコープ遷移

ハマった

Windows batスクリプトの知見

  • setlocal / endlocal しない限り、環境変数のスコープが共有される

実験

カレントディレクトリを指す環境変数 CD で例にとる

構成

|`- main.bat
 `- subdir
     `- sub.bat

main.bat

@echo off
echo [main.bat] current directory: %CD%
call .\subdir\sub.bat
echo [sub.bat] current directory: %CD%

sub.bat

@echo off
echo [sub.bat] current directory: %CD%

rem sub.batがいるディレクトリまで移動
cd %~dp0

echo [sub.bat] current directory: %CD%

実行結果

E:\work\example>main.bat
[main.bat] current directory: E:\_work\example
[sub.bat] current directory: E:\_work\example
[sub.bat] current directory: E:\_work\example\subdir
[main.bat] current directory: E:\_work\example\subdir

E:\work\example\subdir>

子供のbatでcdした結果はコマンドプロンプト含む呼び出し元にも影響。

Windows batで元のCDを残す

【1: setlocal / endlocalで環境変数のローカルスコープをリセット】

sub.bat

@echo off
setlocal
echo [sub.bat] current directory: %CD%

rem sub.batがいるディレクトリまで移動
cd %~dp0

echo [sub.bat] current directory: %CD%
endlocal

実行結果

E:\work\example>main.bat
[main.bat] current directory: E:\_work\example
[sub.bat] current directory: E:\_work\example
[sub.bat] current directory: E:\_work\example\subdir
[main.bat] current directory: E:\_work\example

E:\work\example>

【2: CDを記憶し帰り際にCDしなおす】

sub.bat

@echo off
echo [sub.bat] current directory: %CD%
set PARENT_CD=%CD%

rem sub.batがいるディレクトリまで移動
cd %~dp0

echo [sub.bat] current directory: %CD%

cd %PARENT_CD%

実行結果

E:\work\example>main.bat
[main.bat] current directory: E:\_work\example
[sub.bat] current directory: E:\_work\example
[sub.bat] current directory: E:\_work\example\subdir
[main.bat] current directory: E:\_work\example

E:\work\example>

Linux bashシェルスクリプトの知見

  • /bin/bashで別プロセスで コールされる(別プロセスが立ち上がる)ときは親プロセスのスコープを引き継ぎ、プロセスを戻るときに環境変数破棄
  • .source で呼び出されたら親プロセス内で処理されたことになり、共有スコープとなる(戻っても変更内容引継ぎ)

実験

ディレクトリ構成

|`- main.sh
 `- subdir
     `-sub.sh

main.sh

#!/bin/bash
echo [main]pwd: `pwd`
echo

cd ~/test
/bin/bash ./subdir/sub.sh
echo [main]pwd: `pwd`
echo

cd ~/test
. subdir/sub.sh
echo [main]pwd: `pwd`
echo

cd ~/test
source subdir/sub.sh
echo [main]pwd: `pwd`

sub.sh

#!/bin/bash
echo [sub]pwd: `pwd`
cd ~/test/subdir
echo [sub]pwd: `pwd`

実行結果

yu@yu:~/test$ ./main.sh
[main]pwd: /home/yu/test

[sub]pwd: /home/yu/test
[sub]pwd: /home/yu/test/subdir
[main]pwd: /home/yu/test

[sub]pwd: /home/yu/test
[sub]pwd: /home/yu/test/subdir
[main]pwd: /home/yu/test/subdir

[sub]pwd: /home/yu/test
[sub]pwd: /home/yu/test/subdir
[main]pwd: /home/yu/test/subdir

/bin/bashで蹴られたときだけcdしたものが自動で破棄。

また、$0 で見えるキックされたスクリプト名も、プロセスを共有していると親のものが参照される。