sh (/bin/sh) による配列操作

例えば、表を引いて値を変換しなければならないといったケースでは、sh スク リプトで1次元配列が欲しくなるときがあります。

korn sh や最近の bash では1次元配列をサポートしていますし、複雑な操作な ら、awk や perl といった外部のプログラミング言語を起動するのが簡単ですが、 /bin/sh や POSIX 1003.1 の sh の枠組でも、位置パラメータを使うと、比較的 素直な扱いができます。

インデックス値に対応する値を求める

例えば、12 の要素 Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec を持つ1次元配列を考え、そのインデックスを 1, 2, 3, .. 12 とします。この 配列の i 番目の要素を変数 v の値として取り出す操作として最も簡単なのは、 次の方法ではないかと思います。

  i=8   # 例えば、8 番目の要素を v に代入する
  if [ $i -lt 1 -o $i -gt 12 ]
  then
        echo "i の値が範囲外 (1 - 12)"
        exit 1
  fi
  set dummy Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
  shift $i
  v=$1
  echo $v
変数 i には 1 から 12 のインデックス値を入れておきます。dummy は、i の値 をデクリメントしなくても良いようにするための仕掛けです。

eval を使えば、次のように直接 i 番目の要素を取り出すこともできます。

  i=8   # 例えば、8 番目の要素を v に代入する
  if [ $i -lt 1 -o $i -gt 12 ]
  then
        echo "i の値が範囲外 (1 - 12)"
        exit 1
  fi
  set Jan Feb Mar Apr May Jun Jul Aug Aep Oct Nov Dec
  if [ $i -ge 10 ]
  then
    i=`expr $i - 9`
    shift 9
  fi
  v="v=\$$i"
  eval $v
  echo $v

1次元配列の内容に対応するインデックス値を求める

上記と同じ配列に含まれる特定の値に対応するインデックス値を求める場合は、 もうすこし面倒になりますが、次の方法が素直でしょうか。変数 v には検索し たい要素の値を入れておくと、変数 n に対応するインデックス値が得られます。

  v="Aug"       # 例えば、Aug が何番目かを求める
  set Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
  n=1
  while [ $# -gt 0 ]
  do
        if [ $1 = $v ]
        then
                break
        fi
        n=`expr $n + 1`
        shift
  done
  if [ $# -eq 0 ]
  then
        echo "要素 $v は存在しません。"
        exit 1
  fi
  echo $n

expr を使うのがいやという場合は、次のような方法もあります。

  v="Aug"       # 例えば、Aug が何番目かを求める
  set Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
  n=1
  while [ $# -gt 0 ]
  do
        if [ $1 = $v ]
        then
                break
        fi
        n="$n x"
        shift
  done
  if [ $# -eq 0 ]
  then
        echo "要素 $v は存在しません。"
        exit 1
  fi
  set $n
  n=$#
  echo $n

応用 - 月の末日を求める

# 年月から、その月の末日を求める
y=`/bin/date +%Y`	# 年
m=`/bin/date +%m`	# 月
set 31 28 31 30 31 30 31 31 30 31 30 31
if [ `expr $y % 4` -eq 0 -a `expr $y % 100` -ne 0 -o `expr $y % 400` -eq 0 ]
then
  set 31 29 31 30 31 30 31 31 30 31 30 31
fi
if [ $m -ge 10 ]
then
  m=`expr $m - 9`
  shift 9
else
  m=`expr $m + 0`
fi
eval dmax=\$$m	# 月の末日
echo $dmax

平林 浩一, 1997-08-14