Linuxシェルスクリプトで配列を操作する方法 – パート8
配列という概念がないプログラミング言語は考えられません。実装方法は言語ごとに異なりますが、配列は類似したデータも異なるデータも、ひとつのシンボリックな名前のもとにまとめて管理できる強力な仕組みです。
本記事はシリーズ第8回として、シェルスクリプトにおける配列の使い方を、実際に動かせるサンプルスクリプトとともにわかりやすく解説します。
配列の初期化と使い方
最近のバージョンのbashは1次元配列をサポートしています。配列はシェル組み込みコマンドのdeclareを使って明示的に宣言できます。
declare -a var
ただし、必ずしも上記のように宣言する必要はありません。次のように、個々の要素を直接代入することも可能です。
var[XX]=<value>
ここで「XX」は配列のインデックス(添字)を表します。配列の要素を参照するには、波括弧(中括弧)構文を使用します。
${var[XX]}
注意: 配列のインデックスは必ず0から始まります。
配列全体を一度に初期化する方法
配列全体をまとめて初期化する便利な方法として、丸括弧を使う書き方があります。
var=( element1 element2 element3 . . . elementN )
さらに、インデックスを指定して値を代入する方法もあります。これは前述の方法の派生形です。
array=( [XX]=<value> [XX]=<value> . . . )
また、シェル組み込みコマンドのreadを使えば、スクリプトの実行時にユーザー入力から配列へ値を読み込むこともできます。
read -a array
この文が実行されると、スクリプトは入力を待ち受けます。配列の要素はスペース区切りで入力してください(改行では区切れません)。値を入力し終えたらEnterキーで確定します。
forループによる配列の走査
配列の各要素を順番に処理するには、forループが便利です。
for i in "${array[@]}"
do
#各要素に $i としてアクセス...
done
以下のスクリプトは、これまでに説明した内容をひとつにまとめたものです。
#!/bin/bash
array1[0]=one
array1[1]=1
echo ${array1[0]}
echo ${array1[1]}
array2=( one two three )
echo ${array2[0]}
echo ${array2[2]}
array3=( [9]=nine [11]=11 )
echo ${array3[9]}
echo ${array3[11]}
read -a array4
for i in "${array4[@]}"
do
echo $i
done
exit 0
配列に対するさまざまな操作
文字列向けの標準的な操作の多くは、そのまま配列にも適用できます。次のサンプルスクリプトは、配列に対する代表的な操作(文字列操作を含む)を実装したものです。
#!/bin/bash
array=( apple bat cat dog elephant frog )
#最初の要素を表示
echo ${array[0]}
echo ${array:0}
#すべての要素を表示
echo ${array[@]}
echo ${array[@]:0}
#最初の要素を除くすべてを表示
echo ${array[@]:1}
#範囲を指定して要素を表示
echo ${array[@]:1:4}
#最初の要素の長さ
echo ${#array[0]}
echo ${#array}
#要素数
echo ${#array[*]}
echo ${#array[@]}
#部分文字列の置換
echo ${array[@]//a/A}
exit 0
このスクリプトを実行すると、次のような出力が得られます。
apple apple apple bat cat dog elephant frog apple bat cat dog elephant frog bat cat dog elephant frog bat cat dog elephant 5 5 6 6 Apple bAt cAt dog elephAnt frog
出力内容はコードのコメントと対応しているため自明ですが、補足すると「${array[@]:1:4}」はインデックス1から4個分の要素を取り出す操作、「${#array[@]}」は配列の要素数を取得する操作、「${array[@]//a/A}」は全要素内の「a」を「A」に置き換える操作です。文字列操作については、必要であればシリーズの別パートで詳しく取り上げます。
コマンド置換と配列の組み合わせ
コマンド置換とは、コマンド(または複数のコマンド)の出力結果を別のコンテキストへ代入する機能です。配列の文脈では、コマンドの出力を配列の個々の要素として格納できます。構文は以下のとおりです。
array=( $(command) )
デフォルトでは、コマンド出力内の空白で区切られた各項目が、それぞれ独立した要素として配列に格納されます。次のスクリプトは、指定ディレクトリの中からパーミッション755のファイルだけを一覧表示する例です。
#!/bin/bash
ERR=27
EXT=0
if [ $# -ne 1 ]; then
echo "Usage: $0 <path>"
exit $ERR
fi
if [ ! -d $1 ]; then
echo "Directory $1 doesn't exists"
exit $ERR
fi
temp=( $(find $1 -maxdepth 1 -type f) )
for i in "${temp[@]}"
do
perm=$(ls -l $i)
if [ `expr ${perm:0:10} : "-rwxr-xr-x"` -eq 10 ]; then
echo ${i##*/}
fi
done
exit $EXT
2次元配列のシミュレーション
1次元配列を使えば、2次元の行列を簡単に表現できます。行優先(row major order)の表現方式では、行列の各行の要素が配列のインデックスに沿って順番に格納されていきます。m×n行列の場合、変換式は次のように書けます。
matrix[i][j]=array[n*i+j]
以下は、2つの行列を読み込んで加算し、結果の行列を表示するサンプルスクリプトです。
#!/bin/bash
read -p "Enter the matrix order [mxn] : " t
m=${t:0:1}
n=${t:2:1}
echo "Enter the elements for first matrix"
for i in `seq 0 $(($m-1))`
do
for j in `seq 0 $(($n-1))`
do
read x[$(($n*$i+$j))]
done
done
echo "Enter the elements for second matrix"
for i in `seq 0 $(($m-1))`
do
for j in `seq 0 $(($n-1))`
do
read y[$(($n*$i+$j))]
z[$(($n*$i+$j))]=$((${x[$(($n*$i+$j))]}+${y[$(($n*$i+$j))]}))
done
done
echo "Matrix after addition is"
for i in `seq 0 $(($m-1))`
do
for j in `seq 0 $(($n-1))`
do
echo -ne "${z[$(($n*$i+$j))]} "
done
echo -e "
"
done
exit 0
まとめ
シェルスクリプトにおける配列には多次元対応などの制限がありますが、それでも多くの場面で非常に有用です。特にコマンド置換と組み合わせた活用は強力で、システム管理の観点から見ると、GNU/Linuxシステム上の数多くのバックグラウンド処理スクリプトが、この配列の概念によって支えられているといえます。
-
Linuxシェルスクリプト入門:変数の基本を徹底解説(第9回)
これまでLinuxシェルスクリプティングに関する連載記事をお届けしてきましたが、当時は多くの方にご好評いただき、今でも十分に役立つ内容となっています。ここでは、変数の仕組み、実行方法、そしてシェルスクリプトでの実装について詳しく見ていきます。 コマンド出力の保存 コマンドの出力は標準出力やファイルにリダイレクトできるだけでなく、変数に格納することも可能です。コマンドの出力が大きすぎて画面に収まらない場合は、ファイルまたは変数に出力を保存するしかありません。変数に出力を保存する利点の一つは、確認速度が速いことです。変数はメモリ上に保存されるため、ファイルからの読み出しと比べて高速に処理されます。
-
実用的な5つの例で学ぶBashスクリプト入門チュートリアル
Unix SedやUnix Awkシリーズと同様に、本シリーズではBashスクリプティングに関する記事を複数回にわたって公開します。実用的なサンプルを交えながら、Bashスクリプティングのテクニックを幅広く解説していきます。 シェルとは、ユーザーが入力したコマンドを解釈して実行するプログラムです。コマンドはユーザーが直接入力するか、「シェルスクリプト」と呼ばれるファイルから読み込まれます。 ユーザーからの入力を直接読み取る場合、そのシェルは対話型(インタラクティブ)シェルと呼ばれます。 一方、ファイルからコマンドを読み込んで実行する場合は非対話型(ノンインタラクティブ)シェルと呼ばれます。この