業務のシェルスクリプトで、ファイルの各行についてほげほげ~な処理をすることはよくあると思いますが、std::for_each()
的な「リスト型の各要素についてコールバック関数をマッピングする」メソッドのファイル版があったらな~と思い、実装してみました。
for_each_line関数の仕様
第1引数で指定したファイルの各行について、第2引数に指定したコールバック関数を適用します。その際、事前にコメント行やコメント部分、空行は事前に除外することで、コールバック関数には有効行のみが渡るようにしました。
また、ファイルの読み込みには、ファイルが巨大サイズの場合を考慮してwhile read
を使用しています。
#!/bin/bash
#
# Apply callbackfn to each line in file
#
for_each_line ()
{
local in_file="$1"
local callbackfn="$2"
while read line; do
local line="${line//#*/}"
[[ -z "${line}" ]] && continue
"${callbackfn}" "${line}"
done <"${in_file}"
}
for_each_line関数の使い方
すごくすっきりした!コールバック万歳。
1行のsplit
についてはプロセスfork
を抑止するために、awk
やcut
などの外部コマンドは使わないようにしています。
#!/bin/bash
set -u
. ./lib/libsample.sh
#
# Callback function applied to each line in file
#
callbackfn()
{
set -f; set -- $1; set +f
[[ $# -ne 3 ]] && return
echo 1=$1 2=$2 3=$3
}
for_each_line "./sample.lst" "callbackfn"
$ cat ./sample.lst
#
# sample.lst
#
100 aaa xxx
200 bbb #yyy
300 ccc zzz
$ ./sample.sh
1=100 2=aaa 3=xxx
1=300 2=ccc 3=zz
コマンドの実行結果を読み込む版
ファイルを読み込むのではなく、コマンドラインの標準出力結果を一時ファイルを媒介せずに各行処理したい場合の汎用関数です。第1引数には、結果にコールバック関数を適用したいコマンドラインを文字列リテラルで指定します。
#
# Apply callbackfn to each line in command execution result
#
for_each_line_cmdresult()
{
local cmdline="$1"
local callbackfn="$2"
while read line; do
[[ -z "${line}" ]] && continue
"${callbackfn}" "${line}"
done < <(${cmdline})
}
callbackfn()
{
#TODO : 1行についての処理をここに書く
}
# カレント配下の全ファイルについて、コールバック関数を適用する
for_each_line_cmdresult "find ./ -type f" "callbackfn"
ワンシェルで完結させるコードスニペット
使い捨てのスクリプトを書くのに、いちいち外部モジュールを作るのは逆に手間なので、ワンスクリプトで記述するコードも貼りつけておきます。find
やawk
の実行結果からの読み込みは、Process Substitution(プロセス置換)という機能を使っています。
#!/bin/bash
#awkでファイルのコメント部分と空行を削除した結果を1行ずつ処理
while read line; do
set -f; set -- ${line}; set +f
[[ $# -ne 3 ]] && continue
echo "1=$1 2=$2 3=$3"
done < \
<(cat ./sample.lst | awk '! /^[ \t]*(#|$)/ {sub("#.*$",""); print}')
#!/bin/bash
#カレント配下にあるすべてのファイルパスについて処理
while read line; do
#TODO : 1行についての処理をここに書く
done < <(find ./ -type f)
参考サイト様
ありがとうございます。
シェルスクリプトの中で1行ずつ変数を分割する際には、cutとかawkとか余計なプロセスを起動せずsetを使って分割した方が効率的(双六工場日誌)