シェルスクリプト ライブラリ 『ether.sh』

仕事でシェルスクリプトを使う機会がほとんどなくなったので完全に放置しているが、シェルスクリプトの応用技術としてそれなりに有用な情報になっていると思うのでこのまま公開する。 (2007-09-12)

ether.sh について

ether.sh ダウンロード

右クリックから「リンク先を名前を付けて保存」を選択してください。ファイルの文字コードは UTF-8、改行コードは LF で保存されています。

実行する環境に合わせて、適宜変更してください。

今までありそうでなかったシェルスクリプトのライブラリを作成してみた。まだまだ開発途中なので、使えそうな機能と使えないかもしれない機能が混在している。

各機能も細かい不具合があるかもしれないが、とりあえず一段落したので公開してみることにした。

※ このライブラリの使用は、各自の自己責任でお願いします。

このライブラリは以下の2つを目的として作成されている。

  1. 関数での機能隠蔽によるスクリプトの可読性の向上
  2. 配列操作等の強力な機能の提供

ether.sh ではあえて関数にする必要がないような処理も関数化されているが、それはこの “1” を実現するためのものである。

例えば、入力値が数字かどうかを判定する処理をライブラリなしで記述すると次のようになる。

echo -n "数字を入力してください: "
read KEY
if echo "$KEY" | grep -E '^[0-9]+$' >/dev/null 2>&1; then
  echo "数字が入力されました."
else
  echo "ERROR: 数字が入力されていません."
fi

この程度であればまだ問題はないかもしれないが、上記のようなパイプ処理が増えるにつれてスクリプトの可読性は低下していく。

同様の処理を ether.sh ライブラリを使用して記述すると次のようになる。

echo -n "数字を入力してください: "
read KEY
if _IsNumeric "$KEY"; then
  echo "数字が入力されました."
else
  echo "ERROR: 数字が入力されていません."
fi

同じ判定でもライブラリを使用した方が読みやすいと感じてもらえたのではないだろうか。

ちなみに関数名の先頭にある _ は、その関数が何も出力しないことを意味している

ライブラリ内には _IsNumeric() と同様の機能を持った IsNumeric() も定義されているが、こちらは実行結果を標準出力に出力するようになっている。

以下、ether.sh の主な機能を紹介する。

ファイル操作関数

ファイル読み込み関数

テキストファイルから1行ずつ読み込んで処理を行いたい場合に使用する関数。

通常は while 文にテキストファイルをリダイレクトし、read コマンドで読み込む方法を用いられることが多いが、この方法だと複数ファイルを扱えないことと、処理中は標準入力を占有されてしまうという欠点がある。

ether.sh はこの欠点を解消し、同等の機能を提供する関数を備えている。

GetLine 変数名 ファイル名

→ 関数 GetLine() に読み込んだ行を設定する変数名と読み込み対象ファイルを指定する。

関数は実行されるとファイルの先頭から一行読み込み、指定した変数に読み込んだ行の内容を設定する。再び実行されると前回読み込んだ行の次の行を読み込み、同様にして変数に読み込んだ行の内容を設定する。

関数はコールされるたびに次々と行を読み込み、読み込み対象ファイルの最下行に達すると終了ステータス 2 で終了する。

同時に読み込めるファイル数に制限はなく、現在読み込み中の行位置も各ファイルごとに保持される。

以下は実際に GetLine() を使用したシェルスクリプト (getline.sh) の例。

#!/bin/bash

. ./ether.sh

while GetLine line1 small.txt
do
  GetLine line2 big.txt

  echo "small.txt: $line1"
  echo "big.txt  : $line2"
done

exit 0

上記のシェルスクリプト getline.sh の実行例は以下のとおり。

$ cat small.txt
1
2
3
4
5
6
7
8
9
10
$ cat big.txt
101
102
103
104
105
106
107
108
109
110
#↑読み込み対象となるのは上記の2ファイル。

$ ./getline.sh
small.txt: 1
big.txt  : 101
small.txt: 2
big.txt  : 102
small.txt: 3
big.txt  : 103
small.txt: 4
big.txt  : 104
small.txt: 5
big.txt  : 105
small.txt: 6
big.txt  : 106
small.txt: 7
big.txt  : 107
small.txt: 8
big.txt  : 108
small.txt: 9
big.txt  : 109
small.txt: 10
big.txt  : 110

演算関数

変数のインクリメント

++ 変数名

→ 関数 ++() に対象のとなる変数の変数名を指定する。

C言語などでおなじみの演算子 ++ のシェルスクリプト版である。関数としての実装なので、当然ながら後置 変数名 ++ は不可となる。

使用例は以下のとおり。なお、以下の例からは事前に ether.sh が読み込まれているものとする。

$ i=0
$ echo $i
0
#↑変数 i の値は 0 。

$ ++ i
$ echo $i
1
#↑インクリメント後は 1 になっていることが確認できる。

変数のデクリメント

-- 変数名

→ 関数 –() に対象のとなる変数の変数名を指定する。

C言語などでおなじみの演算子 -- のシェルスクリプト版である。関数としての実装なので関数 ++() と同様に、後置 (変数名 --) は不可となる。

使用例は以下のとおり。

$ i=10
$ echo $i
10
#↑変数 i の値は 10 。

$ -- i
$ echo $i
9
#↑デクリメント後は 9 になっていることが確認できる。

変数への加算

+= 変数名 加算値

→ 関数 +=() に加算対象変数名と加算値を指定する。

$ i=10
$ echo $i
10
$ += i 50
$ echo $i
60

変数への減算

-= 変数名 減算値

→ 関数 -=() に減算対象変数名と減算値を指定する。

$ i=100
$ echo $i
100
$ -= i 30
$ echo $i
70

配列操作関数

配列へ要素を追加

ArrayPush 配列名 要素

→ 関数 ArrayPush() に配列名と追加する要素を指定する。

指定した配列の末尾に要素を追加する関数。この関数を使用することで、分かりづらい bash の配列操作を簡潔に記述することができる。

$ ArrayPush array "hoge"
$ ArrayPush array "fuga"
$ ArrayPush array "foo"
$ ArrayPush array "bar"
$ ArrayPush array "HOGE HOGE"
$ echo "${array[@]}"
hoge fuga foo bar HOGE HOGE

配列から先頭の要素を取り出す

ArrayPop 配列名

→ 関数 ArrayPop() に値を取り出したい配列の配列名を指定する。

指定した配列の先頭から要素を1つ取り出して出力する関数。取り出した要素は配列から削除される。配列から取り出す要素がなくなると、関数は何も出力せずに、終了ステータス 2 で終了する。

$ array=("hoge" "fuga" "foo bar")
$ echo "${array[@]}"
hoge fuga foo bar
$ ArrayPop array
hoge
$ ArrayPop array
fuga
$ ArrayPop array
foo bar
$ ArrayPop array
$ echo $?
2

配列の要素数をカウントする

ArraySize 配列名

→ 関数 ArraySize() に要素数を調べたい配列名を指定する。

指定した配列の要素数を出力する関数。

$ array=("hoge" "fuga" "foo bar")
$ echo "${#array[*]}"
3
#↑配列の要素数は ${#hoge[*]} でも参照可能。

$ ArraySize array
3

配列の全要素を分かりやすい形で出力する

print_r 配列名

→ 関数 print_r() に配列名を指定する。

主にデバッグに使用することを目的とした配列内の全要素出力用の関数。指定した配列の全要素を対応するインデックスと共に、分かりやすい形で出力する。

$ array=("foo" "bar" "hoge" "fuga" "HOGE HOGE" "FUGA FUGA")
$ print_r array
array[@]
 |
 |-[0] = [foo]
 |-[1] = [bar]
 |-[2] = [hoge]
 |-[3] = [fuga]
 |-[4] = [HOGE HOGE]
 |-[5] = [FUGA FUGA]

$ array[100]="END OF ARRAY"
#↑配列にインデックスを飛び番で値を設定する。

$ print_r array
array[@]
 |
 |-[0] = [foo]
 |-[1] = [bar]
 |-[2] = [hoge]
 |-[3] = [fuga]
 |-[4] = [HOGE HOGE]
 |-[5] = [FUGA FUGA]
 |-[100] = [END OF ARRAY]
#↑インデックスが飛び番でも対応可能。