sed と正規表現
あるディレクトリーにある共有ライブラリーを列挙して、
$ ls android-ndk-r5/platforms/android-5/arch-arm/usr/lib/lib*.so android-ndk-r5/platforms/android-5/arch-arm/usr/lib/libGLESv1_CM.so android-ndk-r5/platforms/android-5/arch-arm/usr/lib/libGLESv2.so android-ndk-r5/platforms/android-5/arch-arm/usr/lib/libc.so android-ndk-r5/platforms/android-5/arch-arm/usr/lib/libdl.so android-ndk-r5/platforms/android-5/arch-arm/usr/lib/liblog.so android-ndk-r5/platforms/android-5/arch-arm/usr/lib/libm.so android-ndk-r5/platforms/android-5/arch-arm/usr/lib/libstdc++.so android-ndk-r5/platforms/android-5/arch-arm/usr/lib/libthread_db.so android-ndk-r5/platforms/android-5/arch-arm/usr/lib/libz.so
ここからファイル名だけ抜き出したい、ということで sed を使おうとおもったんだけれど、なにぶん sed に不慣れなものでとても苦労したという話。
最終的に置き換えに使う正規表現は s/.*\/\(.*\)/\1/ になった。
$ ls android-ndk-r5/platforms/android-5/arch-arm/usr/lib/lib*.so | sed 's/.*\/\(.*\)/\1/' libGLESv1_CM.so libGLESv2.so libc.so libdl.so liblog.so libm.so libstdc++.so libthread_db.so libz.so
/\([^/]\+\/\)\+\(.*\.so\)/
対象の文字列のパターンは xxx/ がなんどか繰り替えされ、 xxx.so というライブラリー名が残る、というものにみえる。
だから「/ を含まない文字列の繰り返しに / を連結した文字列」の繰り返しと、残りの .*\.so という具合に考えたんだけれど、 sed の正規表現 ([^/]+\/)+ が xxx/ の繰り返しにマッチしてくれない。
グループをつくるには () をエスケープせねばならない。そして一回以上をの繰り返しをあらわす + もエスケープしないといけないようだ。さらに選択マッチングの [] の中では / はエスケープしなくてもよいよう。そんなこんなが xxx/ の繰り返しへのマッチングを難しくしてくれたわけだ。
もろもろをあわせて最終的に、 xxx/ の繰り返しと残りの xxx.so という形にマッチングさせる sed の正規表現は /\([^/]\+\/\)\+\(.*\.so\)/ ということがわかった。
ふたつめのグループを取り出せばよいので 's/\([^/]\+\/\)\+\(.*\.so\)/\2/' を sed に渡すと、やはりライブラリーの名前だけが取り出せる。
$ ls android-ndk-r5/platforms/android-5/arch-arm/usr/lib/lib*.so | sed 's/\([^/]\+\/\)\+\(.*\.so\)/\2/' libGLESv1_CM.so libGLESv2.so libc.so libdl.so liblog.so libm.so libstdc++.so libthread_db.so libz.so
まあでも、 / が出てくるまで .* で任意文字列の最長マッチをして、残りの .* を取り出す s/.*\/\(.*\)/\1/ がシンプルでいいやね。