あるあるおハマリ大事典 - bashのevalとセミコロン

今日のおハマリは、とあるシェル関数の出力をevalするスクリプトです。

このシェル関数は、bashの配列を定義してそれをexportするものです。ですので、いつくかの配列がexportされるのが期待する結果です。

いまだに挙動がよくわかったようなわかってないようななんで、おしえてえらいひと!


比較のために、

  • シェル関数: 行末にセミコロンをつけるかつけないか
  • eval: するっとevalするか、evalする関数を呼ぶか

の組み合わせで 4 通り試します。

こんなスクリプトを:

call=$1
semicolon=$2

if [ "$semicolon" = "ari" ]; then
  evalme() {
    cat <<EOF
HAHAHA_A=(Ei); export HAHAHA_A;
HAHAHA_B=(Bi); export HAHAHA_B;
HAHAHA_C=(Ci); export HAHAHA_C;
EOF
  }
elif [ "$semicolon" = "nasi" ]; then
  evalme() {
    cat <<EOF
HAHAHA_A=(Ei); export HAHAHA_A
HAHAHA_B=(Bi); export HAHAHA_B
HAHAHA_C=(Ci); export HAHAHA_C
EOF
  }
else
  echo abort1
  return 1
fi


if [ "$call" = "direct" ]; then
  #set -x
  eval $(evalme)
  #set +x
elif [ "$call" = "func" ]; then
  func_do_eval() { eval $(evalme); }
  #set -x
  func_do_eval
  #set +x
else
  echo abort2
  return 1
fi

引数を変えてsourceして、3つの配列 HAHAHA_A, B, Cが定義されるかみてみます:

$ unset HAHAHA_A HAHAHA_B HAHAHA_C; declare -x|grep HAHAHA; \
  . sourceme.sh direct ari; declare -x|grep HAHAHA
declare -ax HAHAHA_A='([0]="Ei")'
declare -ax HAHAHA_B='([0]="Bi")'
declare -ax HAHAHA_C='([0]="Ci")'

$ unset HAHAHA_A HAHAHA_B HAHAHA_C; declare -x|grep HAHAHA; \
  . sourceme.sh direct nasi; declare -x|grep HAHAHA
declare -ax HAHAHA_A='([0]="Ei")'
declare -ax HAHAHA_B='([0]="Bi")'
declare -ax HAHAHA_C='([0]="Ci")'

$ unset HAHAHA_A HAHAHA_B HAHAHA_C; declare -x|grep HAHAHA; \
  . sourceme.sh func ari; declare -x|grep HAHAHA
declare -ax HAHAHA_A='([0]="Ei")'
declare -ax HAHAHA_B='([0]="Bi")'
declare -ax HAHAHA_C='([0]="Ci")'

$ unset HAHAHA_A HAHAHA_B HAHAHA_C; declare -x|grep HAHAHA; \
  . sourceme.sh func nasi; declare -x|grep HAHAHA
declare -ax HAHAHA_A='([0]="Ei")'

最初の3つは、A,B,Cの3つの配列がexportされているので期待した結果です。

んがっ!最後のはなんでかAしかexportされてません。
set -xすればexportされない理由はなんとなくわかるんですが。。。んじゃなんで2つめの直接evalったときはexportされるのでしょうか。。。

$ bash --version
GNU bash, version 3.2.39(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2007 Free Software Foundation, Inc.

ス。