PC-Blues # 037 ホームページを全部まとめて書き換えるはなし。

  教訓。獅子はうさぎ一羽にもその全力を投ずるという。

  ふとしたきっかけでmidiサイトヴァー チャルうたごえ喫茶「のび」とかかわりをもつことになった。

  ようするにこのサイトのmidiが〈bgsound〉タグで書かれていたために当家で再生できなかったのである。じつはのちにw3mは〈bgsound〉 タグを再生できることに気づいたのだが、インターネットエクスプローラ専用だと思いこんでいたのである。

  そこで掲示板に「〈bgsound〉タグなのでmidiが聞けません。〈embed〉か〈object〉にしてくだされば、聞けます。」という旨を書いた ところ、ウェブマスターのエーちゃんが誠実な人で一見のわたしに「じゃあ〈embed〉にしましょう」といってくれた。「でも200もファイルがあるので どうしよう」とのこと。

  こういうのはsedやperlの重宝するところである。Linuxにしろ、FreeBSDにしろUNIXの眷属にはsedが標準装備である。 例えば、chijounohoshi.html の中の
<bgsound src="midi/chijyonohoshi.mid" loop="1"> を
<embed hidden="true" src="midi/chijyonohoshi.mid" loop="1"> に
書き直すくらいのことは

sed 's/<bgsound/<embed hidden=\"true\"/' chijonohosi.html > chojonohoshi.html.tmp
mv chijonoshi.html.tmp chijonoshi.html

  でおわりである。sedの s/A/B/は文字列AをBで書き換える。

  サイト全部のファイルという場合は、とりあえずwgetでサイトをまるごとコピーしてしまう。

wget -m -np http://village.infoweb.ne.jp/~fwgc6260/

  200 ファイルとなると 実行命令自体をファイルにしてしまうと便利註1であ る。この場合さきほどの s/// コマンドは別ファイルにしておけばいろんな書き換えをこれひとつでできる。そこでscr というファイルにすることにする。 sedは sed -f scr とすれば、scr ファイルから動作を読みこむ。 まず、scr をつくる。エディタで書いてもいいが、なんせ一行だから註2

echo 's/<bgsound/<embed hidden=\"true\"/' > scr

  で充分である。つぎは実行ファイルをつくる。

ls *.html|sed 's/\(.*\)/sed -f scr  \1 >\1.tmp/' > list1
ls *.html|sed 's/\(.*\)/mv \1.tmp \1/' > tmp2html

  ここで 「.」は任意の一文字、「*」は直前の文字の0回以上の繰り返しを表現し、その結果 「.*」 は任意の文字列を表すが、これを 「\(」 と 「\)」 で囲んでおけば後で書出すことができる。この囲みは 「\1」 で書出すことでき、複数の 「\(」「\)」 があるときは順次 「\2」 ,「\3」 ,「\4」などで書出せる。上記二つのコマンドを実行してやると、list1はこんなかんじである。

sed -f scr  anatagayoake.html >anatagayoake.html.tmp
sed -f scr anohito.html >anohito.html.tmp
:
〈中略〉
:
sed -f scr zawameki.html >zawameki.html.tmp
sed -f scr zensekaiminshu.html >zensekaiminshu.html.tmp

  いっぽうtmp2htmlはこんなかんじになる。

mv anatagayoake.html.tmp  anatagayoake.html
mv anohito.html.tmp anohito.html
:
〈中略〉
:
mv zawameki.html.tmp zawameki.html
mv zensekaiminshu.html.tmp zensekaiminshu.html

  あとは

sh list1

  とすれば、list1が実行され、一時ファイルに書き換えができる。

  書き換えのデキを確認して問題なければ、

sh tmp2html

  として、ファイル名を本来の名前にもどせばよい。これでできあがったファイルを送ったところとても感謝された。わずか1分の手間でこれはオトクである。と ころが、である。<bgsound>ではloop="1" は『繰り返し = 一回』の意であるが、<embed>では『繰り返し=オン』である。"false" と "true" で記述することになっているが "0" と "1" でも動作するようである。よって

echo 's/loop=\"1\"/loop=\"false\"/' >scr
sh list1 && sh list2

  となる。これは30秒の手間くらいか。シェルでは && は前のコマンドがエラーにならず実行できたら後のコマンドも実行するという機能である。なお、s///では「"」が引用符として解釈されないように直前に 「\」をいれてエスケープしている。

  ところが、である。これで送ったところ、htmlの<head>〜</head>に<embed>タグをいれると ページの頭に不明の空行がはいってしまうそうである。こうなると空行が気にならないページのおしりに移動したほうがよさそうである。移動となると単純な書 き換えというわけにはいかない。ちょっとは頭をつかう必要がある。エディタでscrを開き、

  scr :


/<embed/{
h
d
}
/<\/body>/{
g
p
s/^.*$/<\/body>/
}

  とした。最初の/<embed/{…}は「<embed」と書いてある行を探し、その行に対してだけ 「{」から「}」までの操作をするという意味である。hはその行を保存する。dはその行を削除する。つづいて/<\/body>/で 「</body>」と書いてある行を探し、gで保存した「<embed 」の行をひっぱり出して、pで書き込む。その後この行ををもういちど「</body>」と書き直す。こんどはpと書かなくても操作終了時に書 込まれる。

  ところがである。こんどはこれを送ると改行がされていない、とのこと。さらに行末に不明な文字が多数追加されたとのこと。unixでの改行は"\n"とい う文字で表現されているが、DOSやwindowsでは"\r\n"である。先程の操作で追加された行の改行が"\n"だったために、通常のDOSテキス ト文書とウインドウズの「メモ帳」が認知しなかったため"\r"が不明な文字と化し、改行もなされなかったようである。ここらへん「ひで丸エディタ」は各 種文字コード、改行文字に対応しているので問題なかったようである。

  こういう場合はnkf を使うと便利である。nkf はシフトJIS、JIS、EUC-JPの各種文字コードの変換に使われるが、改行文字の変更もできる。文法はこうである。

nkf -c  < 元ファイル > 宛先ファイル (unix改行 → dos改行)
nkf -d < 元ファイル > 宛先ファイル (dos改行 → unix改行)

  これも全ファイル分の実行ファイルをつくる。上のlist1のときと同様に

ls *.html|sed 's/\(.*\)/nkf -c  < \1 >\1.tmp/' > unix2dos
ls *.html|sed 's/\(.*\)/nkf -d < \1 >\1.tmp/' > dos2unix

  とすればよい。これで準備OK

  呪文は長くなって

sh dos2unix && sh tmp2html && sh list1 && sh tmp2html && sh unix2dos && tmp2html

  である。毎回やってられない長さだが、Linuxのbashは[↑]キーで以前打ったコマンドが出てくるのでまあどうということはない。

  ところがである。<embed>タグでは、win98あたりではインターネットエクスプローラがフリーズする、という現象が報告された。これ は当家では再現しないが、年来の訪問者から続々「どうしたんですか。」である。これではいたしかたない。JavaSciriptを使いインターネットエク スプローラをその他のブラウザで動作を変えるしかない。

  再度<embed>をヘッダに移動し、これをこんなふうに変えた。

<SCRIPT type="text/javascript">
<!--
if (navigator.appName == "Microsoft Internet Explorer")
{
document.write("<bgsound src=\"air.mid\" loop=\"1\">")
}
else
document.write("<embed loop=\"false\" hidden=\"true\" autostart=\"true\" src=
\"air.mid\">")
//-->
</SCRIPT>
</head>
<NOSCRIPT><bgsound loop="1" src="air.mid"></NOSCRIPT>

  しかし、sedは行志向のエディタなので、前の行の記述を後にもってくるのは簡単だが、後の行を前にもってくるのはアレコレと工夫が必要になる。註3邪魔くさいので一旦全ての改行をとりはらって前方へ送ってみた。却って手間だっ たかもしれないが。今回はsed ではなくtrを使う。それというのも私の使用しているsedは置き換えで改行文字を出力できないから註4である。

ls *.html|sed 's/\(.*\)/tr "\n" "\r"  < \1 >\1.tmp/' > n2r
ls *.html|sed 's/\(.*\)/tr "\r" "\n" < \1 >\1.tmp/' > r2n

  上は\nを全て、\rにする。下はその逆である。 とりあえずscrをエディタでいじって、

  scr :


s/^\(.*\)\(<\/head>"\r"\)\(.*\)\(<embed[^>][^>]*>\)/\1\4\2\3/

  とした。ここで[^>]は「>」以外文字を示す。「<embed[^>][^>]*>」は「<embed」で はじまり、任意の文字を含み、「>」で終わる文字列を表す。だから、この「\1」はファイル頭から「</head>」まで、「\2」は 「</head>」、「\3」は「</head>」の次の行から、「<embed ……>」まで「\4」は「<embed ……>」である。それ以降は「</body>」と「</html>」しかないがそのまま出力されるはずである。ただし 「</head>」の行がその後に何の文字もなしに改行されていなければならない。しかも「</HEAD>」と大文字で書いて あってもアウトである。よい子はマネしてはダメである。なお</head>を<\/head>と表現してあるのはs///で区切 に使っている「/」と文中の「/」が衝突するのでこういう場合は「\/」と表現することになっている。ただし区切文字のほうを換えてs|||などとしても よい。

  さて呪文は

sh dos2unix
sh tmp2html
sh n2r
sh tmp2html
sh list1
sh tmp2html
sh r2n
sh tmp2html

  ええ加減 && でつなぐのも面倒になってひとつずつ実行したりして。

  これで一番下にいった「<embed ……>」が「</head>」の前の行に戻ってきた。さてこれより件のjavascriptを挿入する。

  こんどのscrは

  scr :


/<embed/{
h
i\
<SCRIPT type=\"text\/javascript\">\
<!--\
if (navigator.appName == \"Microsoft Internet Explorer\")\
{
p
g
s/\"/\\\"/g
s/<embed hidden=\\\"true\\\" loop=\\\"false\\\"\(.*>\)/document.write("<bgsound loop=\\\"1\\\" \1\")/
a\
}\
else
g
s/\"/\\\"/g
s/<embed hidden=\\\"true\\\" loop=\\\"false\\\"\(.*>\)/document.write("<embed loop=\\\"false\\\" hidden=\\\"true\\\" autostart=\\\"true\\\" \1\")/p
i\
\/\/-->\
<\/SCRIPT>
g
s/<embed hidden=\"true\" loop=\"false\"\(.*>\)/<NOSCRIPT><bgsound loop=\"1\" \1<\/NOSCRIPT>/
}

  である。なんだかだんだん大がかりになってきたような気がする。なお、javascript内で「"」を表現する場合はこれも「\"」としてやらなければ ならないので、sedのs///のほうでは「\\\"」となる。

  すでにunix改行になっているので実行コマンドはこうである。

sh list1
sh tmp2html
sh unix2dos
sh tmp2html

  うん思わずに長い闘いであった。われながら、ヘッポコプログラマにでもなったようである。

  ところがである。 <script>は<head>〜</head>でよいが、<noscript>は< body>〜</body>にいれるのが作法である。<noscript>は<script>が実行されな かった場合にはいる文章を書くものであるからそれはまあそうである。今回のような<bgsound>タグをいれるというのは例外的であろう。 そんなわけでこのソースをIBMホームページビルダでさわるとエラーがでるのだそうである。さすが、IBM。 例の『訪問者に優し いウェブサイトづくり』によれば、ネットスケープコンポーザについでまともなHTMLを出力する数少ないHTMLエディタだそうである。というわ けで<body>タグを<NOSCRIPT>の前にいれる。

  まずはいまある<body>タグをとる。

echo 's/<body [^>][^>]*>//' >scr
sh list1 && sh tmp2html

  つぎに<NOSCRIPT>のある行の前に<body>をいれる。今回のscrはこうである。

  scr


/<NOSCRIPT>/{
h
s/.*/<body>/p
g
}

  これでどうにかいけるか、と思ったら「喫茶のび」のウェブマスターがいちいちわたしに依頼を出すのも手間になったか、遠慮したか、全ファイル一括書き換え に興味をもったらしく、windows環境で使用できるか、と問い合わせがきた。sedはMSDOS版が10年あまり前からあるのだが、lsはDOSでは dir /yくらいであろうか。あいにくDOSコマンドははっきりいって忘れているうえにDOS窓はUNIXのシェルに比較してあまりにバカである。ここは Perlを使って貰うほうがはなしが速い。Perlを薦めてついでに次のようなスクリプトをサンプルにわたした。

  rewrite.pl


###########ここから############################
#!/usr/bin/perl
#註)汝の責任にて使用されたし。
#これを使用してどんな損害がでても作者は責任をもてません。
$input="html"; # 入力するファイルの拡張子
$output="html"; # 出力するファイルの拡張子
$inputdir="."; # 入力するファイルのディレクトリィ
$outputdir="$inputdir/output"; # 出力するファイルのディレクトリィ
#####################################################################
#註) 拡張子か、ディレクトリィを入力と出力でずらすようにしてください。
########################################################################
opendir DIR ,"$inputdir"; # 入力ディレクトリィを開く。
@dir = readdir DIR ; # 入力ディレクトリィを読む。
@list = grep {/^.+\.$input/} @dir; # xxxx.htmlというファイルだけにする。
closedir DIR ; #入力ディレクトリィを閉じる。
unless(-d $outputdir){ #出力ディレクトリィがなければ作る。
mkdir $outputdir,0700;
}
foreach(@list){ # xxxx.htmlのxxxxの部分だけとりだす。
s|^(.*).$input|$1|;
@file = (@file ,$_);
}
foreach(@file){
#すべての入力ファイルについて以下の動作をくりかえします。
open IN ,"$inputdir/$_.$input"; #入力ファイルをオープンする。
open OUT ,">$outputdir/$_.$output"; #出力ファイルをオープンする。
while(<IN>){
s|<embed (.*)src=(.*)|<object $1data=$2|;
# ↑ここで
#入力ファイルを一行読んでは書き換えて(またはそのまま)出力ファイルに書き込みます
# s|書き換える文字列の正規表現|書き換え後の文字列| とします。改行は"\r\n"です。
# たとえば、s|おばさん|おねえさん|gと書けば文章中のすべての「おばさん」が
#「おねえさん」になります。
# 正規表現はいろんなエディタで使えますから覚えておくとトクします。
# これは「 <embed ナンタラ src= カンタラ 」という文字列を
#「<object ナンタラ data= カンタラ 」という文字列に書き換えています。
print OUT $_;
}
close IN; #入力ファイルを閉じます。
close OUT; #出力ファイルを閉じます。
}

###########ここまで############################

  くらいであっさり済んだではないか。先見の明がないとはこのことか。

  教訓。ちょっとオーバーパワーくらいのツールを使うほうが結局ラクなことがある。 勉強になりました。


註 1
別にこんなものつくらないでも
mkdir new
for i in *.html
do sed -f scr $i > new/$i
done
mv new/*.html .
で充分ではないか。後悔さきにたたず。くううう。
註 2
どうせ、scr ファイルをつくるのならその一行目に "#!/usr/bin/sed -f " と書いて実行権限をつけておけば
mkdir new
for i in *.html
do ./scr $i > new/$i
done
mv new/*.html .
で充分ではないか。後悔さきにたたず。くううううううう。
註 3
やっぱりちょっと調べものをして工夫してみた。こっちのほうがシンプルだったかもしれない。
#!/usr/bin/sed -f
/<\/head/{
# </headとある行を見たら行動開始。
:begin
#loopのはじまり。
/embed/{
#もしembedと書いた行にあたったら、
s/<\/head>\(.*\)\(<embed .*\)/\2\
<\/head>\
\1/
}
# </head> ナンタラ <embed>を <embed></head>ナンタラ に並べかえる。
t
# 置き換えが完了したらループは終了。
N
# そうでなければ、次の一行を読みこんで
b begin
# loopのアタマへいく。
}

註4
うそ、もしくは無知である。sedで改行するときは sed 's/ここで改行します。→/ここで改行します。→\
 /' このように\のあとに改行すればよいだけである。
[前へ] [次へ]
[Home] [目次]

2003/5/18