GAWK 複数ファイルの一括ブロック置換
あるフォルダに定型テキストファイル群があり、それぞれのファイルの特定箇所で同内容の置換が必要だった場合、行単位なら、grep を使用します。これが特定ブロック単位での置換であって、恒常的に行うものとなると、筆者は GAWK とバッチファイルを使用します。
AWKスクリプトもバッチファイルも記述は短く、とても単純なのですが、元ファイル側の準備が大変です。元ファイル作成時のテンプレートに、あらかじめ変更/更新予定箇所としてのマーカー(始まりと終わり)が設置されていれば、いいのですが。
例) htmlファイル群の特定ブロック一括置換
C:\test フォルダ内の構成と概要
バッチファイルを実行すると、3つの .html ファイルの同一ブロック(記述位置はどこでもよい)が、new_block.txt の内容に置換/更新されるというものです。
sample001~003.html 被置換ファイルの内容
sample001.html
1 : <!DOCTYPE html><body>
2 : <h1>Hsample001</h1>
3 : <!--sub-->
4 : <li>old subject01</li>
5 : <li>old subject02</li>
6 : <!--/sub-->
7 : <h1>Fsample001</h1>
8 : </body></html>
sample002.html
1 : <!DOCTYPE html><body>
2 : <h1>Hsample002</h1>
3 : <p>exp002</p>
4 : <!--sub-->
5 : <li>old subject01</li>
6 : <li>old subject02</li>
7 : <!--/sub-->
8 : <h1>Fsample002</h1>
9 : </body></html>
sample003.html
1 : <!DOCTYPE html><body>
2 : <h1>Hsample003 Menue</h1>
3 : <!--sub-->
4 : <li>menue01</li>
5 : <li>menue02</li>
6 : <!--/menue-->
7 : <h1>Fsample003 Menue</h1>
8 : </body></html>
sample001.html 3行目と6行目が今回のブロック置換のかなめ、マーカーです。両マーカー共に各ファイル中でユニークでなければなりません。一般テキストの場合は、不自然でない感じのユニークなマーカーを工夫して配置します。
sample003.html 3行目は故意に間違えています。(メニューエントリー項目)
new_block.txt 置換ファイルの内容
1 : <!--sub-->
2 : <li>new subject01</li>
3 : <li>new subject02</li>
4 : <li>new subject03</li>
5 : <li>new subject04</li>
置換終了位置マーカーは書きません。ダブります(AWKスクリプト参照)。
開始マーカー後の内容及び行数は自由に記述でき、元ファイルのそれらとは無関係です。
replace_block.bat
1 : @echo off
2 : rem htmlファイルのブロック置換 (errorlevel遅延展開)
3 : pushd %0\..
4 : setlocal enabledelayedexpansion
5 : for /r %%n in (*.html) do (
6 : gawk414 -f replace_block.awk %%n new_block.txt > _temp_out.txt
7 : if !errorlevel! == 0 (type _temp_out.txt > %%n)
8 : )
9 : endlocal
10 : del _temp_out.txt
フォルダ内すべての .html ファイルを置換し、上書きします。このとき、対象マーカーがない、または、マーカーが不完全、もしくは過多な .html ファイルがあれば、そのファイルは元ファイルのまま、更新されません。
5行目 for の後の /r は、このバッチファイルがあるディレクトリを起点として、再帰的にサブフォルダー内の .html ファイルについても、ブロック置換を実行するためのものです。
他のページにもちらほら書いていますが、AWKPATHにカレントディレクトリの相対パスを設定していない場合は、「replace_block.awk ~そんなファイルはありません」と GAWK に怒られます。
replace_block.awk
BEGIN
1 : #. replace_block.awk;
2 : # gawk -f replace_block.awk file new_block_file
3 : #. BEGIN;被置換ファイル確認 new_block先読み
4 : BEGIN {
5 : while (getline < ARGV[1] > 0) {
6 : if ($0 ~ /^<!--sub-->$/) ++smark; #開始マーカー
7 : if ($0 ~ /^<!--\/sub-->$/) ++emark; #終了マーカー
8 : }
9 : close(ARGV[1]);
10 :
11 : #対象外ファイル
12 : if (smark != 1 || emark != 1) exit 1; #BAT !ERRORLEVEL!
13 :
14 : while (getline < ARGV[2] > 0) new[++i] = $0;
15 : close(ARGV[2]);
16 : }
ACTION_01
17 : #. Action;new_block置換
18 : FILENAME == ARGV[1] {
19 : if ($0 ~ /^<!--sub-->$/) nop = 1;
20 : if ($0 ~ /^<!--\/sub-->$/) nop = 0;
21 :
22 : if (!nop) print $0; #nop中は転写しない
23 : else if (!once) { #1度だけ実行
24 : for (i in new) print new[i];
25 : once = 1;
26 : }
27 : }
実行結果(replace_block.bat)
省力化に貢献する一括ブロック置換ですが、状況によっては、とても危険(元に戻せない)ですので、マーカー配置ミスによるデータの消失をある程度防ぐようにしました。
for %n in (*.html) do type %n
ですが、普段はこんなことしません。type *.html
とします。が、やたらと改行が多く、画面に納まらなかったんです。