ファイルに記載された文字列の置換
時々 2ch を覗くのですが、このような質問が書かれてあったので、練習として解いてみます。 最近はモダンなプログラム記法というのが Perl を筆頭に流行っているようですが、awk でも gawk 標準のライブラリがあり、include によりこれを有効に活用することで少しだけモダンな awk プログラムになります。
2ch の他の方の解答にもありますが、特に以下の点に少し気を使ってみました。
- ファイル名をオプションで指定する
- ファイルの有無をチェック
- 置換文字列のペアがあることをチェック
- gawk 用の length() の使い方と delete の使い方で解く
- それでも awk らしく
完成したものは以下のようなものです。
#! /usr/bin/igawk -f
# 2ch_551.awk
# http://pc11.2ch.net/test/read.cgi/unix/1224085718/551
# require getopt and join library functions
@include getopt.awk
BEGIN {
# オプションの処理
while ((option = getopt(ARGC, ARGV, "t:s:d:")) != -1) {
if (option == "t") {
input_file = Optarg;
check_file(input_file);
}
if (option == "s") {
before_file = Optarg;
check_file(before_file);
}
if (option == "d") {
after_file = Optarg;
check_file(after_file);
}
}
# 置換前文字列の読み込み
while (getline < before_file > 0) {
before_arr[++i] = $0;
}
close(before_file);
# 置換後文字列の読み込み
while (getline < after_file > 0) {
after_arr[++j] = $0;
}
close(after_file);
# 置換前後の対象数が同じであることをチェック
if (is_same_length(before_arr, after_arr) == 0) {
print "配列 " a " と配列 " b " の長さが異なります" > "/dev/stderr";
exit 1;
}
# ARGV を再設定する
# input_file を getline で読んでも構わないが、少しだけ awk らしく?
delete ARGV;
ARGV[1] = input_file;
}
{
# 格納した配列で置換を行っていく
for (i in before_arr) {
if ($0 == before_arr[i]) {
sub(before_arr[i], after_arr[i], $0);
}
}
# 置換したものを出力
print $0;
}
# usage: 使用方法の説明
function usage() {
print "igawk -f 2ch_551.awk -t input -s before -d after" > "/dev/stderr";
}
# check_file: ファイルの有無をチェック
function check_file(file) {
if (getline < file <= 0) {
print file " というファイルがありません" > "/dev/stderr";
usage();
exit 1;
}
close(file);
}
# is_same_length: 配列 a と配列 b の長さが同じかどうかチェック
function is_same_length(a, b) {
if (length(a) == length(b)) {
return 1;
} else {
return 0;
}
}
少し長めのプログラムになっていますが、その分、わかりやすくしているつもりです。 かなりパートに分解していますので、簡略化するのも、仕様を少しくらい複雑にすることも楽なようにしています。
なお、実行は include を含んでいますので、igawk で実行します。
$ cat a.dat 111 AAA BBB CCC 222 $ cat b.dat AAA BBB CCC $ cat c.dat DDD EEE FFF $ igawk -f 2ch_551.awk -t a.dat -s b.dat -d c.dat 111 DDD EEE FFF 222


