前回の(1)はこちらから。
シェルコマンドを組み合わせる
(1)では、Perlの初歩的な構文を使って作成したツールを紹介しました。(2)では、そこにシェルコマンドを組み合わせた例を紹介します。
openコマンドを組み合わせる
筆者は普段Macを使用していますが、Macのシェルにはopen
というコマンドがあり、ターミナル操作ではこれをよく使います。
open
コマンドは、引数にファイル名やURLを入れるとそれらを開いてくれます。
system関数を使う
では、このopen
コマンドをPerlのコードから使うにはどうすればよいでしょうか。
Perlでシェルのコマンドを呼び出す方法はいくつかありますが、筆者は戻り値を使う場合はバッククオートを、戻り値を使わない場合はsystem
関数を使っています。
今回のopen
コマンドでは戻り値を使わないので、system
関数で次のように書きます。
複数のWebサイトを一斉に開く
では次に、これを前節で紹介したfor
文、if
文、__DATA__
のセットと組み合わせてみましょう。
このコードを実行すると、__DATA__
の下に入れたURLのWebページが次々とブラウザで開かれます。
筆者は編集や執筆の仕事をする際、その資料として多くのURLを手もとにストックしていますが、これを一度に全部確認したいとき、ブラウザにURLを貼り付けたり、どこかにリンク集を作成して1つずつクリックしたりするのではなく、一度実行するだけですべてのURLを開けるこのコードを使います。
複数ファイルの行数や文字数をカウントする
件の音楽全集では、メインコンテンツとして毎回3万字弱の座談会記事を作成していました。しかしその素材となる文字起こしは8~10万字という膨大なものだったので、これを1本のファイル上で編集していくのではなく、まず大まかなトピックごとにファイルを分割し、それぞれのファイルで編集を行ってから1本にマージして仕上げていました。
この際、各ファイルの進捗を把握するために、それらの行数や文字数をカウントしたいと考えて作ったのが次のツールです。
コードには、シェルコマンドのwc
やawk
が含まれています((1))。当初はその部分を含めて.bashrc
の関数[1]として書いていたのですが、その他の繰り返し処理や正規表現なども含めてすべてシェルスクリプトで書くのは自分には難しいと判断し、それら周辺的な部分はPerlで書くことにしました。
使い方
では、使ってみましょう。なるべく手軽に使えるように、ここではlwcというエイリアスを設定しておきます。
前述の分割したファイルを収めたディレクトリでツールを実行すると、各テキストファイルの行数、文字数、それらの合計が出力されます[2]。
こうした長丁場の仕事では、進捗の把握がとても重要です。1つのコマンドを実行するだけで十数本の原稿の状況を一望できるこのツールは、作業の大きな助けになりました。
ほかのツールを組み合わせる
ここまでに、Perlの初歩的な構文で作ったツール、シェルコマンドを組み合わせたツールを紹介しました。次に紹介するのは、ほかのプログラマーが開発、公開したツールを組み合わせた例です。
ターミナル操作を楽にする
俗に「黒い画面」と呼ばれるターミナルは、非エンジニアにとって怖い対象とされがちですが、筆者はファイルの検索や操作を行う際もキーボードから手を離さずに済むという理由で、MacではFinderよりもターミナルをよく使います。
しかし、ターミナル操作でも不便な場面は少なくありません。たとえば、ディレクトリを移動するたびに毎回cd
コマンドを打つのは面倒ですし、ディレクトリ内に似た名前のファイルが複数ある場合には[Tab]キーでの補完が効きづらく、次のようなファイル群から目的のファイルを選択する際には、Finderを使ったほうが手早く開けます。
このような問題に悩んでいるときに、牧大輔(lestrrat)さんによるpeco
や、mattnさんによるcho
といったコマンドラインツールの存在を知りました。peco
は標準入力から渡された行をインクリメンタルに絞り込み、選択した結果を標準出力に返すシンプルなツールです。また、cho
はそのシンプルさをさらに追求し、絞り込み機能を排して[J]キーまたは[K]キーだけで直感的に行を選択できるツールです。これらを自分のプログラムに組み合わせることで、上述の悩みを解消できるのではないかと考えて作ったのがchoco
です。
.bashrcの設定
choco
を使うためには、事前にいくつか.bashrc
の設定をしておく必要があります。
まず、Perlのコードからカレントディレクトリの位置を操作しても実際のシェルは影響を受けないので、ディレクトリ間の移動を行うための関数を用意します((1))。また、筆者はこのツールを1日に何度も使用するので、最も打ちやすい[J]キーにエイリアスを割り当てています((2))。この際、不可視ファイルを表示するオプションを付けたエイリアスも設定し((3))、用途によって使い分けています。
使い方
では、実際に使ってみましょう。今回は後述の英語学習ゲームcarvo
のリポジトリを例に用います。ルートディレクトリでコードを実行すると、次のようにpeco
の選択画面に切り替わり、ディレクトリ内のファイルとサブディレクトリが一覧表示されます。
QUERY
欄((1))で対象を絞り込んだり、選択行を上下に移動したりしながらファイルを選択すると、そのファイルパスが返ります。このときにオプションでopen
コマンドを指定しておくと、選択されたファイルが開きます。ディレクトリを選択した場合は、その中に入って同じことを繰り返します。上の階層へ移動したいときは../
((3))を選択し、終了する場合にはexit
((2))を選択します。
前述のとおり、選択画面にはデフォルトでpeco
が使われていますが、オプションでcho
を指定することもできます。エイリアスで設定する場合は、次のようにします。
choco
は今や筆者にとってなくてはならない存在ですが、その中身はwhile
文やif
文など、初歩的な構文の組み合わせによる100行にも満たないプログラムです。主要な機能を外部ツールに任せたことにより、初めて実現できたツールだと言えます。
特定の文字列を含むファイルを検索する
choco
の応用として、find-word.pl
というツールも作りました。
これはカレントディレクトリ配下を対象に、特定の文字列を含むファイルを再帰的に検索するツールです。
編集の仕事をしていると、「あの語句はどのファイルに記述したっけ……」と探したくなることがたびたびあります。そのようなときに、MacのFinderで検索するのは効率が悪く、かといって心当たりがあるファイルを一つ一つ開いていくのも現実的ではありません。
しかしこのツールを使えば、下階層のすべてのファイルを対象に、短時間で目当てのファイルを探していくことができます。
使い方
対象となるプロジェクトのルートディレクトリでコードを実行します。この際、デフォルトでは選択したファイルのパスを返すだけなので、筆者はopen
コマンドでファイルを開くようにオプションを指定し、これをfw
というエイリアスに設定して呼び出しています。
たとえば、先ほどのリポジトリ内でYAML(YAMLAin't Markup Language)を扱っているファイルを探したい場合、ターミナルにfw yaml
と打ち込めば、yaml
という文字列を含むファイル名とその行の情報が一覧表示されます。
この一覧画面はpeco
で表示しているので、ここからさらに絞り込みながら対象を選択できます。選択されたファイルは、前述のopen
コマンドによって開かれます。
このツールでは日本語の検索もできるので、筆者は表記揺れの検出器としてもよく使っています。
ゲーム感覚で英単語を学習する
これまでに紹介したツールは、おもに業務の効率化や負担軽減を目的としていましたが、ここで少し趣が異なるプログラムを紹介します。
Perlの入門初期にwhile
文や標準入出力の方法を教わると、必ずと言ってよいほど練習問題として出題されるのがじゃんけんゲームです。
シンプルなコードですが、自分が入力を行うまで相手が何を出してくるかわからないこのしくみは、どこか人間の好奇心を根本から刺激するようなおもしろみを感じさせます。あるとき、これを筆者が以前から取り組んでいた英単語の暗記学習に活かせないかと考えて作ったのが、英単語学習ゲームcarvo
です。
使い方
ターミナルでコードを実行すると、peco
のUI(UserInterface)で学習コースを選択する画面が現れます。
ここで好きなコースを選択すると、コマンド選択画面に移動します。
この画面から、選択ツールとしてcho
が使われています。[J]キーまたは[K]キーで上下に移動してコマンドを選択します。play
((1))を選択するとゲームが始まり、次のように英単語と5つの和訳候補が現れるので、英単語に該当すると思う選択肢を選びます。
選択が正解ならポイントが加算され、不正解なら間違えた単語が誤答リストに加わります。終了するには、コマンド選択画面でexit
((2))を選択します。
小さなコードの積み上げ方
carvo
はじゃんけんゲームと同様、while
文を中心とする単純な構造のプログラムなので、初めは1本の小さなファイルに収まっていました。しかしそのうち、単語リストをYAMLファイルで管理したくなったり、機能ごとにファイルを分割したくなったりと、手を入れるたびに新しい書き方を試したくなり、結果的に今回紹介した中で最も行数が多いプログラムになりました。
コードの量が多くなれば、たとえ小さな変更でも想定外の影響が生じる可能性が高くなり、手を入れることが難しくなっていきます。このような状況に対して、筆者は普段からコード片を検証するためのファイルを1本用意しておき、そこで事前に動作を確認するようにしています。
このファイルで扱う内容はさまざまです。配列の要素数を知るにはどうすればよいのか((1))、正規表現の先読み/後読みはどう書くのか((3))、配列の要素として配列を使うことはできるのか((4))など、些細ながら認識が曖昧だった部分を確認するために、以前に書いたコードの上に__END__
((2))を挿入して、その上に最小のコードを書いて実行します。
__END__
はその下にあるコードを一括でコメントアウトしてくれるトークンです[3]。この1行を入れるだけで、その下に書かれた以前のコードを消すことなく無効化してくれるので、筆者はこれを重宝しています。
こうして事前に挙動を確認してから本番のコードに手を入れるようにすると、想定外のエラーを減らすことができます。筆者はターミナルでp
と打てばこのファイルが立ち上がるようにエイリアスを設定しています。
<続きの(3)はこちら。>
- 特集1
イミュータブルデータモデルで始める
実践データモデリング
業務の複雑さをシンプルに表現!
- 特集2
いまはじめるFlutter
iOS/Android両対応アプリを開発してみよう
- 特集3
作って学ぶWeb3
ブロックチェーン、スマートコントラクト、NFT