xyzzyのフィルタコマンドであっと言う間に仕事を終わらせる

(インスパイア元: Emacs のキーボードマクロであっと言う間に仕事を終わらせる (フェンリル | デベロッパーズブログ))


こんにちは。お仕事PCをEmacsキーバインドにして周りから冷たい目で見られる担当のser1zwです。

今日はみんな大好きなxyzzyでテキストファイルを処理するときに役立つフィルタコマンドを紹介します。

どんな時に役に立つの?

ふだんの暮らしの中で、CSVのような行を中心としたテキストデータを扱う機会がよくあると思います。たとえば、1行に3つデータがあるCSVで、いちばん左のデータだけを削除したい場合、どうするでしょうか?

たとえばこんなファイルです。

abcd,ef,ghi
jk,lmno,pqr
stu,vwx,yz0
...(2000行くらい続く)

これを次のようにしたいわけです。

ef,ghi
lmno,pqr
vwx,yz0
...(2000行くらい続く)

手作業で1つひとつ心をこめて編集するとか、見なかったことにするとかいろいろと選択肢はありますが、ここではあえて、xyzzyと適当なプログラミング言語さえわかっていればできてしまうxyzzyのフィルタコマンドを使ってみましょう。

フィルタコマンドとは

フィルタコマンドは、xyzzyのバッファ内またはリージョンの内容を標準入力につなげて、任意のコマンドを実行し、その出力結果に置き換えることができる機能です。
バッファ内すべてを対象としたい場合は filter-buffer (C-x #) を、リージョンのみを対象としたい場合は filter-region (C-x |) を実行します。
任意のコマンドとしてPerlやRubyなどのワンライナーを実行することで、割と何でもできます。

何をするか決める

今回のような作業をフィルタコマンドで行う手順は次のようになります。

  1. ある行を目的どおり処理するワンライナーを書く(今回はRubyで)
  2. すべての行に適用する

では、1行目を例にどんな処理が必要か考えてみましょう。

abcd,ef,ghi

この行から、次のように1つめのデータを削除したいわけです。

ef,ghi

Rubyistなら考える前に手が動きますね。

print 'abcd,ef,ghi'.split(',')[1..-1].join(',') #=> ef,ghi

これをxyzzyのバッファで実行すればよさそうですね。

さっそく実行してみる

心の準備はいいでしょうか?

では、C-x # で filter-buffer コマンドを実行してみましょう。実行するとカーソルがミニバッファに移動します。ここに書かれたコマンドに対して、バッファの内容が標準入力から渡され、実行されます。

f:id:ser1zw:20120118030933p:image

実行するコマンドはこんな感じにしてみます。

ruby -ne "print $_.split(',')[1..-1].join(',')"

Rubyの-e オプションを設定すると、ファイルではなくコマンドラインから渡されたスクリプトを実行します。

-n オプションを設定すると、プログラム全体が

while gets
  ...
end

で囲まれているように動作します*1Rubyのオプションの詳しい話はこのへんとかを見てください。

標準入力の内容は、組み込み変数 $_*2 を使って取り出しています。

Rubyに -n オプションを指定して実行するため、 $_ にバッファの内容が1行ずつ入ってきます。そこで、 $_ に対して1行ごとに実行したい処理を適用すればOK、というわけです。

ためしてみる

C-x # を実行してミニバッファに書いたコマンドは、そのままEnterキーを押せば実行できます。さっそく適当なバッファで試してみましょう。

f:id:ser1zw:20120118034541p:image

うまくいきましたか? うまくいかない場合は、もう一度ワンライナーを見直してみましょう。C-x # を再度実行し、C-p (上キーでもOK)を押すと、実行したコマンドの履歴が見れますので、よーく見て直してください。

選択したリージョンのみに適用!

うまくいったら、今度は選択したリージョンのみで実行するようにしてみます。ここでわざわざ新しくバッファを作って必要な部分だけコピペして filter-buffer!!というのはちょっと野暮なのでやめておきましょう。

まずは適用したい行をすべてリージョン指定(選択)します。そして、おもむろに C-x | を押し、先ほどと同じようにコマンドを書きます。

f:id:ser1zw:20120118030935p:image

さぁ、いかがですか?コマンドが選択したリージョンのみに適用されているはずです。ちなみに、C-x | は filter-region というコマンドを呼び出しています。

こうして、1行ずつ手でやっていたら気の遠くなるような作業も一撃で済んでしまうわけです。

まとめ

今回実行したワンライナーはかなり単純なものですが、使う言語でできることなら大概何でもできてしまうので、かなりいろんな場面で使えます。 再利用性など考えず、その場で必要なワンライナーを書いて、使ったら、あとは忘れてしまって構いません。どうせ履歴見れるし。

最近使ったワンライナーとしては、「ずらーっと書かれたデータからSQLを生成する」「ログファイルから必要な列だけ抜き出し、ちょっとだけ加工する」などが役に立ちました。「単純作業だけど、スクリプトを書けば一瞬で終わる。むしろ書かせろ。」という時はxyzzyのフィルタコマンドでさくっと仕事を終わらせて、まわりのみんなにドヤ顔をご披露ください。


参考: xyzzyでプログラミング/空行、行末空白などの削除

あわせて読みたい: Emacs のキーボードマクロであっと言う間に仕事を終わらせる (フェンリル | デベロッパーズブログ)

*1:場合によっては -p オプションにしてもいいかもしれません。この辺はお好みで。

*2:最後に gets または readline で読み込んだ文字列が入っています http://www.ruby-lang.org/ja/old-man/html/_C1C8A4DFB9FEA4DFCAD1BFF4.html