子プロセスで親プロセスの環境変数を変更したい

...が,簡単にはできないらしい.今やりたいことを整理すると: (1) ある bash スクリプト hoge.sh では何か環境変数が定義される,(2) シェル (tcsh) で hoge.sh を実行して,hoge.sh の中で定義される環境変数をシェルで使いたい,(3) hoge.sh は変更しない.
使ってるシェルが bash なら,以下で解決.

. hoge.sh

tcsh だから話がややこしくなってる.子プロセス (hoge.sh) で定義された環境変数を無理やり tcsh で使う方法を考えた.

アイディア

以下のような tcsh スクリプトを書く.

  • bash を(子プロセスとして)実行し,printenv; . hoge.sh; printenv の順に実行.
  • 各 printenv の出力を保存しておき,差分を取って,変更があった環境変数を,親プロセス(tcsh スクリプト)上で再度定義する.

で,この tcsh スクリプトを source で実行すると,hoge.sh で変更される環境変数が,シェル上でも使えるようになる.

実装

copy_bash_env.tcsh:

set bashscript=$1

set tmp=/tmp/tmp$$
\bash -c "\
  printenv > $tmp-a;  \
  . $bashscript;  \
  printenv > $tmp-b"

set newv=`\diff \
    --old-group-format='' \
    --unchanged-group-format='' \
    $tmp-a $tmp-b`

\rm $tmp-a
\rm $tmp-b

foreach n1 ($newv)
  set n2=`echo $n1 | sed 's/\([a-zA-Z_]\+\)=\(.*\)/\1 \"\2\"/g'`
  eval "setenv $n2"
end
  • 余談だが,diff にこんなオプションがあるのは知らなかった.
  • printenv の出力結果は A=B みたいな感じだから,A "B" のように sed で置換する.
  • (環境)変数名が変数に含まれているから,eval を使う.

このスクリプトを,以下のように使う:

source copy_bash_env.tcsh hoge.sh

これで,hoge.sh で変更された環境変数が,このスクリプトを実行したシェル (tcsh) にも反映される.

もっと簡単な方法は...?