2016年07月19日
Perl のワンライナーについて
Movable Type や PowerCMS は、主に Perl というプログラミング言語で開発されています。
Perl を習得することで各種プラグインの実装などができるようになりますが、GNU/Linux, Unix 環境で各種タスクをこなす際にも有用なプログラミング言語ですので、実例にも触れながら perl
コマンドを使ったワンライナーの有用性について Tips を書きます。
製品サポートなどの様々な調査において個人的にも欠かせないツールとなっています。
申し送り事項
掲載されているサンプルは自己責任において、実行前に対象をバックアップするなどして慎重に実行ください。当記事に掲載されたコードを実行することによる問題については、いかなる場合においても一切の責任は負いかねます。
perl
コマンド
Perl プログラムは、 perl
コマンドを使って実行することができます。
Perl プログラムは、ファイルに記述して実行することが多いと思いますが、プログラムを perl
コマンドの引数に直接指定することも可能で、1行のコマンドで様々な処理を実現できます。これをワンライナーと呼んだりします。
perl
コマンドの使い方
perl
コマンドの使い方は下記のコマンドで確認できます。
$ perl -h
また、perldoc
コマンドを使用して、下記のコマンドを実行すると、より詳細なオンラインドキュメントを閲覧できます。
$ perldoc perlrun
perldoc
コマンドは、Perl の各種モジュールに記述された POD
(Plain Old Document) と呼ばれるドキュメントを、ページャを通して表示することができるコマンドで、開発時にとても有用です。
mt:Entry* タグのドキュメントを参照する
$ perldoc $MT_HOME/lib/MT/Template/Tags/Entry.pm
上記のようにパスを指定して対象のモジュールのドキュメントを参照することができたり、
CGI モジュールのドキュメントを参照する
$ perldoc CGI
上記のようにインストール済みのモジュール名を指定して対象のモジュールのドキュメントを参照することができたりします。
perl
コマンドで良く使うコマンドライン引数(スイッチ)
下記のコマンドライン引数は頻繁に使用するため、覚えておくと有用です。perl
コマンドではスイッチと呼びます。
-e
スイッチ
-e
スイッチは、eval
の略で、指定した Perl プログラム文字列を実行することができます。最もよく使うスイッチです。
$ perl -e 'print "Hello, World\n"'
-I
スイッチ
-I
スイッチは、Include
の略で、指定したディレクトリのパスを Perl のライブラリパスとして設定できます。
$ perl -I/path/to/lib -I/path/to/lib2
-M
スイッチ
-M
スイッチは Module
の略で、指定した Perl モジュールをロードします。(use Foo;
相当)
$ perl -MFoo -e '...'
これを使って、よく特定の Perl モジュールがインストール済かを検査したり、特定の Perl モジュールのバージョン情報の確認などをします。
$ perl -MArchive::Zip -e1
上記でエラーがでなければインストール済と判定できます。
$ perl -MArchive::Zip -e 'print "$Archive::Zip::VERSION\n"'
上記で Archive::Zip モジュールのバージョンが確認できます。
=
とつなげることで特定の関数をインポートすることもできます。(use Foo qw(bar);
相当)
$ perl -MFoo=bar -e '...'
-l
スイッチまたは -E
スイッチ
前述の例では print
分の最後に改行コードを追加し、改行ありで出力していますが、実際には出力内容に改行が欲しいことがほとんどなので、-l
スイッチや -E
スイッチを使用すると便利です。
-l
スイッチは、line
の略で、print
文の最後に、改行コードを追加するような挙動になります。
$ perl -le 'print "Hello, World"'
また、Perl 5.10 以降は、say
関数が追加され、print
文でいちいち改行コードを追記する必要がなくなりました。
say
関数のような Perl 5.10 以降の新しい機能を使用する場合は -e
スイッチの代わりに、-E
スイッチを使用します。
$ perl -E 'say "Hello, World"'
Perl 5.10 未満をお使いの場合は -E
スイッチが存在しないため注意が必要です。
以上が後述のスイッチを除き、頻繁に使うスイッチになると思います。上記以外には、日本語を取り扱う上で有用な -C
スイッチなどがあります。
perl
コマンドとパイプの組み合わせ
perl
コマンドはパイプと組み合わせて使用するとさらに有用です。
その際に組み合わせて使用するスイッチも紹介します。
-n
スイッチ
-n
スイッチが何の略称かはわかりません。パイプで渡ってくる出力結果を while (<>) { ... }
で実行するような挙動になります。
例えば、find
コマンドのように .cgi
が拡張子であるファイル一覧を出力するようなケースなどでは下記のように記述します。
$ ls -1 $MT_HOME | perl -nle 'm|\.cgi$| and print'
perldoc perlrun
のサンプルでは、カレントディレクトリにある直近1週間以内に更新したファイルを削除するサンプルが掲載されています。
$ find . -mtime +7 -print | perl -nle unlink
perl
コマンドを awk
コマンドや sed
コマンド代わりに使う
awk
コマンドや sed
コマンドはそれぞれ有用ですが、perl
コマンドで代用することも可能です。
awk
コマンドの代わりに使う
awk
コマンドの代わりとして perl
コマンドを使用する場合は、-F
スイッチと -a
スイッチを覚えると有用です。
例えば、awk
コマンドの help
に記載のあった、/etc/passwd
からユーザー名だけを取り出すサンプルは下記になります。
$ awk -F: '{ print $1 }' /etc/passwd
これを perl
コマンドに置き換えると下記になります。
$ perl -F: -anle 'print $F[0]' /etc/passwd
-F
スイッチは Field speparator
の略で、-a
スイッチは auto split
の略です。
上記の例では、-F
スイッチでセパレータ(今回はコロン)を指定し、-a
オプションでセパレータで区切られたフィールドを @F
配列にセットする挙動になるため、awk
のサンプルと同一の結果になります。
頑張れば CSV ファイルの処理などもできるように思うかもしれませんが、そう単純でもないので、そういう場合は csvkit などの専用ツールを使った方が良いです。
餅は餅屋です。
sed
コマンドの代わりに使う
sed
コマンドの代わりとして perl
コマンドを使用する場合は、-p
スイッチを覚えると有用です。(多分 print
の略)
-p
スイッチを指定すると、パイプで渡ってくる出力結果を while (<>) { ...; print "$_\n"; }
で実行するような挙動になります。-n
スイッチとの併用はできません。
sed
コマンドでよくあるファイルの内容を置換して出力するサンプルが下記です。
$ sed -e 's/MT/PowerCMS/g' file.txt
これを perl
コマンドに置き換えると下記になります。
$ perl -pe 's/MT/PowerCMS/g' file.txt
-p
を指定することで print
+ 改行コードの出力部分を省略できるので、sed
による置換のようになります。
また、GNU sed のように標準出力をリダイレクトすることなく、ファイル内置換を実施するための -i
スイッチ(多分 in place
の略)が perl
コマンドにも存在しており、sed
的なワンライナーに組み合わせると便利です。
上記の例を下記のように実行すると、file.txt
のバックアップが file.txt.orig
として生成されたような動きになり、file.txt
内の MT という文字列は、すべて PowerCMS という文字列に置換されます。
$ perl -pi.orig -e 's/MT/PowerCMS/g' file.txt
正規表現は Perl の仕様になるので、sed
や grep
などでスイッチの違いによる正規表現の細かい仕様差異を知らなくて済むという利点があります。
Movable Type および PowerCMS での応用
以上を踏まえて、Movable Type および、PowerCMS の調査などに役立てるためのいくつかの Tips を紹介します。
カレントディレクトリを $MT_HOME
においた方が何かと都合が良いので、$MT_HOME
(mt-config.cgi
の設置されたアプリケーションディレクトリ)にカレントディレクトリがある前提で説明します。
これ以降の手順は実運用環境では絶対に試さないよう強くお願いいたします。
mt-config.cgi からデータベース関連の情報を取得する
まずは簡単なファイルの読み込みと、テキスト処理から。
$ perl -nle '/^\s*D(?:B|atabase)/i and print' mt-config.cgi
MT のバージョンを取得する
ファイルから直接ではなく、MT のモジュールの機能を使って MT 関連の情報を取り出す場合は、-I
スイッチで、$MT_HOME/lib
と $MT_HOME/extlib
にパスを通した上で、-M
スイッチで MT モジュールを読み込むのが常套句です。
また、MT の各種情報を取り出すためには、MT のインスタンスが必要なので、instance
メソッドで初期化します。
まずは簡単なサンプルで、MT のバージョンを取得してみます。
$ perl -Ilib -Iextlib -MMT -le 'print MT->instance->version_id'
なお、bash
のような高機能なシェルを使っている場合は、ブレース展開が使用できるため、-I
スイッチは下記にまとめられます。
$ perl -I{,ext}lib -MMT -le 'print MT->instance->version_id'
記事タイトルを一覧出力する
MT オブジェクトを操作するオペレーションを個人的には良く使います。下記はブログ ID が 3 のブログから、id の昇順で記事を10件取り出してタイトルを出力しています。日本語がタイトルに含まれる場合は先ほど少し触れた -C
スイッチを組み合わせると便利です。
$ perl -I{,ext}lib -MMT -CO -le 'print $_->title for MT->instance->model("entry")->load({ blog_id => 3 }, { limit => 10, sort => "id", direction => "asc" })'
メールを送る
MT::Mail
モジュールを使ってメールを送るなんてこともできます。
$ perl -I{,ext}lib -MMT::Mail -e 'MT::Mail->send({ To => "me@example.com", Subject => "テスト"}, "テストです\n") or die MT::Mail->errstr'
特定の記事を1万件コピーする
記事を1万件コピーして、パフォーマンスのテストをしたいような時にワンライナーで記事を投入したりできます。例えば記事 ID が 10 の記事を1万件コピーするワンライナーが下記です。(セミコロンで文を区切っているのでワンライナーとは言えないですが…)
$ perl -I{,ext}lib -MMT -e '$orig = MT->instance->model("entry")->load(10); do { $e = $orig->clone; $e->id(undef); $e->basename("post_$_"); $e->save or die $e->errstr } for 1..10000'
番外編
MT のライブラリに限らず、さらに視野を広げると様々な処理が Perl ワンライナーで実現できます。
カレントディレクトリを閲覧する簡易 HTTP サーバを立てる
MT や PowerCMS でも利用している PSGI のアプリケーションサーバを使って、簡易 HTTP サーバを立てて、コンテンツの確認などができます。
$ perl -S plackup -MPlack::App::Directory -e 'Plack::App::Directory->new(root => ".")'
この例は perl
コマンドの例というよりは、plackup
の例になりますが、plackup
コマンドのコマンドラインスイッチは perl
コマンド互換になっています。
アルファサードのリポジトリのリンク一覧を Markdown 形式で取得する
Mojolicious という Perl の Web フレームワークに同梱されている様々な機能は、ojo というワンライナー用のモジュールにて簡易に利用することが可能なので、スクレイピングなどが簡単にできます。
$ perl -Mojo -E 'say g("https://github.com/alfasado?tab=repositories")->dom->find(".repo-list-name > a")->map(sub { sprintf "- [%s](https://github.com%s)", $_->text, $_->attr("href") })->join("\n")'
アルファサードのリポジトリのリンク一覧を Markdown 形式で取得する(GitHub API 版)
上記のスクレイピングは最終手段で、API が用意されている場合は、API を使った方が楽で便利です。
API を使うにしても Perl のワンライナーが有用です。
これも Mojolicious
のようなオールインワンな Perl モジュールを使うと楽にできますが、敢えて、Perl の標準モジュールおよび、標準的なモジュールで実施するなら下記のようになります。(2ページ以降は省略)
$ perl -MLWP::UserAgent -MJSON -e 'printf "- [%s](%s)\n", $_->{name}, $_->{html_url} for @{JSON->new->decode(LWP::UserAgent->new->get("https://api.github.com/users/alfasado/repos")->decoded_content)}'
まとめ
perl
コマンドを習得することで様々な処理がワンライナーで実行できることを確認いただけたかと思います。
紹介したものはいずれも比較的シンプルで用途も限定されたものですが、組み合わせや工夫次第で、様々なタスクに取り入れることができます。
製品サポートではこれらの技術を駆使して様々な調査や作業の省力化を図っています!
Perl を使って様々な困難や問題に立ち向かって問題解決したい方、デキる方、またはそのような人が周りにいる方がいらっしゃいましたら、人材は随時募集しておりますので、ぜひともお声がけください。
- カテゴリー
- 技術情報
コメントを投稿する