« AppleScriptでPerl正規表現置換 | トップページ | OS Xのsedで改行を置換する »

2013年3月29日 (金)

AppleScriptと一行Perlについて

先日の記事で、Apple Script 内において一行 Perl を使って正規表現置換を行うことについて述べたが、その際、

 set new_text to do shell script "/bin/echo -n " & quoted form of old_text & " | perl -e '$_=join \"\",<>; s/abc//g; print;'" without altering line endings

といった例を挙げた。echo からテキストの内容(old_text)を perl コマンドに渡し、perl でそれを一つの文字列に結合してから置換処理を行って出力(print)し、変数 new_text に格納するというものである。

Perl が標準入力から受け取ったテキストは、一行ごとに区切られて配列に入った状態になっている。join で一つに結合する必要があるのは、そのためである。

ところが、実は do shell script は「シェルコマンドによって出力された内容を全部まとめて変数に渡す」という仕様になっている。だから、

 set old_text to "abc\ndef\n"
 set new_text to do shell script "/bin/echo -n " & quoted form of old_text & " | perl -e 'while(<>){s/abc//g; print;}'" without altering line endings

このように一つに結合せずに一行ごとに print したとしても、すべての行が print された結果を一つにまとめたものが new_text に格納されるので、問題なく処理が行える。

では、なぜ先日の例では一つにまとめていたかというと、複数の行にまたがった置換を行いたかったからである。例えば

 set old_text to "abc\ndef\n"
 set new_text to do shell script "/bin/echo -n " & quoted form of old_text & " | perl -e 'while(<>){s/c\nd//g; print;}'" without altering line endings

のような場合、置換はうまく行えない。perl が標準入力から受け取るのは「abc\n」と「def\n」という二つの要素からなる配列である。まず「abc\n」を置換処理して print し、次に「def\n」を処理してプリントするため、「c\nd」はマッチしないのである。(だが、new_text にはその二回の print の結果がまとめて渡されるため、new_text には「abc\ndef\n」が格納される)


とは言え、複数行にまたがった置換を行うつもりがないないので結合の必要はない、という場面も多いだろう。そのような場合は、もう少し別の書き方が可能である。

ありがたいことに、一行 Perl には -n というオプションがあり、それを使って「perl -ne」とすれば、各行ごとに処理してくれる。このオプションを使えば「while」が省略できる。

 set new_text to do shell script "/bin/echo -n " & quoted form of old_text & " | perl -ne 's/abc//g; print;'" without altering line endings

更に言えば、「perl -pe」とすれば、print まで自動で行ってくれるので、

 set new_text to do shell script "/bin/echo -n " & quoted form of old_text & " | perl -pe 's/abc//g;'" without altering line endings

このように、print すら省略できる。

複数行にまたがらない置換の場合は、「perl -pe」を使うのが妥当であろう。


また、複数行にまたがっていたとしても、old_text の改行コードが CR なら「perl -pe」での処理が可能である。

なぜなら、シェルコマンドは改行コード LF だけを「改行」と見なすからである。

 set old_text to "abc\rdef\r"
 set new_text to do shell script "/bin/echo -n " & quoted form of old_text & " | perl -pe 's/c\rd//g;'" without altering line endings

このように書いた場合、perl が受け取るのは「abc\rdef\r」というテキストである。もし「abc\ndef\n」なら、perl は「abc\n」と「def\n」という二つの要素からなる配列と解釈するが、「abc\rdef\r」であれば、単一の要素と解釈し、一度に処理する。だから「s/c\rd//g」による置換が成立するのである。

Mac OS X 標準のクリップボードは、内部的には改行コード CR で処理している。なので、クリップボードから受け取ったデータを一行 Perl で置換するような場合は、複数行にまたがっていたとしても perl -pe で処理が可能である。

例えば、ウェブブラウザ Safari 上で

abc
def

という文字列をコピーし

 set cpContent to get the clipboard
 set outputContent to do shell script "/bin/echo -n " & quoted form of cpContent & " | perl -pe 's/c\rd//g;'" without altering line endings
 display dialog outputContent

このスリプトを実行すると、「abdf」と表示されるはずである。


« AppleScriptでPerl正規表現置換 | トップページ | OS Xのsedで改行を置換する »