Sample Site

GAWK 複数ファイルの一括ブロック置換

あるフォルダに定型テキストファイル群があり、それぞれのファイルの特定箇所で同内容の置換が必要だった場合、行単位なら、grep を使用します。これが特定ブロック単位での置換であって、恒常的に行うものとなると、筆者は GAWK とバッチファイルを使用します。

AWKスクリプトもバッチファイルも記述は短く、とても単純なのですが、元ファイル側の準備が大変です。元ファイル作成時のテンプレートに、あらかじめ変更/更新予定箇所としてのマーカー(始まりと終わり)が設置されていれば、いいのですが。

例) htmlファイル群の特定ブロック一括置換

C:\test フォルダ内の構成と概要

replace_sub_01.jpg

バッチファイルを実行すると、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)

replace_sub_02.jpg

省力化に貢献する一括ブロック置換ですが、状況によっては、とても危険(元に戻せない)ですので、マーカー配置ミスによるデータの消失をある程度防ぐようにしました。

for %n in (*.html) do type %n ですが、普段はこんなことしません。
type *.html とします。が、やたらと改行が多く、画面に納まらなかったんです。