Perl Hackers Hub

第57回 自作ツールによる日常業務効率化―初歩的なコードだけで身近な問題を解決!(1)

この記事を読むのに必要な時間:およそ 3 分

本連載では第一線のPerlハッカーが回替わりで執筆していきます。今回のハッカーは非エンジニアとして普段からプログラミング情報を発信しているnote103こと門松宏明さんで,テーマは「自作ツールによる日常業務効率化」です。

本稿のサンプルコードは,WEB+DB PRESS Vol.112のサポートサイトから入手できます。

簡単なツールで日々を効率化する

筆者は昨年までフリーランスの編集者として活動していましたが,現在はIT企業のカスタマーサポートとして働いています。その一方で,数年前にPerlのプログラミングに出会って以来,Perlを使って自分のためのツールをたくさん作ってきました。本業のプログラマーのように大規模なプログラムや本格的な手法は扱えませんが,基礎的な構文を組み合わせるだけでも驚くほど多様なプログラムを作れることを日々実感しています。

Perlは今なお時代とともに着実な進化を遂げていますが,本稿ではそうした最新の事情にはあえて触れず,基本的なコードによる簡単なツールでも普段の業務や生活を飛躍的に効率化してくれることを紹介します。

なお,本稿ではPerl 5.28.0を使用し,Macでの操作を前提としています。

基礎構文でツールを作る

初めに紹介するのは,for文,if文などの初歩的な構文だけで作成したツールです。

作業の進捗を可視化する

筆者が以前に編集していた音楽全集注1では,毎巻脚注を100~150個ほど作成していました。この作業は地味な割にたいへんな労力を必要とすることから,ペースを落とさずに作り続ける工夫が必要でした。そこで,⁠今日は注釈を何個作った,残りは何個になった」という進捗を可視化し,自らを鼓舞するために書いたのが次のコードです。

footnotes-counter.pl

#!/usr/bin/env perl
use strict;
use warnings;

my $done = 0;
my $doing = 0;
my $todo = 0;

for (<DATA>) {  ――(1)
    if ($_ =~ /\A■/) {    
        $done++;           
    }                      
    elsif ($_ =~ /\A▲/) { 
        $doing++;          |(2)
    }                      
    elsif ($_ =~ /\A□/) { 
        $todo++;           
    }                      
}                          
print "完了: $done, 作成中: $doing, 未着手: $todo\n";

__DATA__    
■見出しA   
本文A       
            
▲見出しB   |(3)
本文B       
            
□見出しC   
本文C       
(省略)      

このプログラムはfor文,if文,__DATA__の組み合わせでできています。__DATA__は,実際のファイルの終端よりも前の場所に,論理的なスクリプトの終端を設定できるトークン注2です。__DATA__以降に置かれたテキスト(3)は,DATAファイルハンドルから読み出すことができます(1)⁠。

このプログラムでは,それをfor文で下方へ流し,if文と正規表現で受け止めて必要な処理を施しています(2)⁠。

使い方

使い方はシンプルです。まず注釈が完成した見出しに■,作成中の見出しには▲,未着手のものに□マークを付けておき,先ほど(3)で示した__DATA__以下にその注釈テキストをペーストして実行すると,次のように進捗を一目で把握できます。

実行例

$ perl footnotes-counter.pl
完了: 17, 作成中: 4, 未着手: 87

このfor文,if文,__DATA__の組み合わせは,ほかにもさまざまな目的に応用できます。次に紹介するのもその一つです。

注1)
commmons: schola(コモンズ・スコラ⁠⁠ ─⁠─ 坂本龍一監修による音楽の百科事典
注2)
文脈によってさまざまな意味を持つ用語ですが,ここでは「しるし」「ラベル」のような意味で用いています。

データの書式を変換する

前述の音楽全集では,毎回テーマに沿った年表を巻末に掲載していました。各巻のテーマはクラシック,ポップス,民族音楽など目まぐるしく変わるので,テーマに特化した項目はゼロから作らなければいけませんが,その他の一般的な音楽史や世界史については以前に作った項目をある程度再利用することができました。

そこで,筆者は過去に作成した年表上のすべての出来事を,次のように1本のテキストファイルにストックしておき,使い回せそうな内容があればそこから取り出して使うようにしていました。

西洋音楽史年表の素材データ(部分)

1920   コルンゴルトのオペラ《死の都》初演
1920   ホルスト《惑星》全曲初演
1922   ヒンデミット《一九二二年》
1922   イベール《寄港地》
1923   ストラヴィンスキー《結婚》初演
1923   オネゲル《機関車パシフィック231》
1924   プッチーニ《トゥーランドット》(未完)
1924   レスピーギ《ローマの松》
1924   ガーシュウィン《ラプソディ・イン・ブルー》初演

しかし,ここで一つの問題が生じます。この素材データでは,内容を整理しやすいように1行ごとに出来事を記し,各行頭に年数を振っていますが,実際の年表では出来事単位ではなく,年単位で年数が振られていたほうが自然です。そのため,本番用のデータは1年につき1行として,そこに複数の出来事がぶらさがるように修正する必要があります。また,可読性を高めるために●マークを追加し,最終的には次のように書き換えなければいけません。

西洋音楽史年表の仕上がり例

●1920年  コルンゴルトのオペラ《死の都》初演  ●ホルスト《惑星》全曲初演
●1922年  ヒンデミット《一九二二年》  ●イベール《寄港地》
●1923年  ストラヴィンスキー《結婚》初演  ●オネゲル《機関車パシフィック231》
●1924年  プッチーニ《トゥーランドット》(未完)  ●レスピーギ《ローマの松》  ●ガーシュウィン《ラプソディ・イン・ブルー》初演

そこで,前者から後者へ年表の書式を変換するために書いたのが次のコードです。

timeline-parser.pl

my @data = <DATA>;
my $year;
my $buf_year = 0;
my $topic;

for (@data) {
    if ($_ =~ /\A(\d+)\t(.+)/) {
        $year = $1;
        $topic = $2;
        if ($year == $buf_year) {
            print " ●$topic";
        }
        else {
            print "\n●$year年 $topic";
        }
        $buf_year = $year;
    }
}

__DATA__
1920   コルンゴルトのオペラ《死の都》初演
1920   ホルスト《惑星》全曲初演
(省略)

__DATA__以下に管理用フォーマットにのっとった年表項目を入れて実行すると,本番用フォーマットに変換されたデータが出力されます。

もしこの変換ツールがなければ,何百行にも及ぶ項目をすべて目視と手作業で書き換えることになったでしょう。単純なコードですが,多くの時間と労力を節約することに貢献してくれました。

ファイル名を簡便にリネームする

もう一つ,Perlの基礎構文を使って作成したツールを紹介します。なお,このプログラムはGitHubで公開しているので,ここでは概要と使い方を中心に解説します注3⁠。

通常,ターミナルでファイルをリネームするときにはmvコマンドを使いますが,このときにはリネーム後のファイル名をすべて入力する必要があるので,bar.txtbaz.txtにするように,1文字だけ変更したい場合には無駄な入力が増えてしまいます。実際にはrzにしたいだけですから,それに見合った操作をできたほうが便利です。そして,これを実現するのがcopyrename.plです。

使い方

このツールでリネームを行う場合は,実行時に引数としてrnameを渡します注4⁠。また,筆者はこの処理をよく行うので,.bashrc注5rrというエイリアスを設定しています。エイリアスとは,複雑なコマンドに簡単な別名を与えることで,コマンドを入力しやすくする機能です。

.bashrc

alias rr='path/to/copy-rename.pl rname'

カレントディレクトリにはapple.mdという名前のファイルが入っています。コードを実行すると,処理対象をファイルかディレクトリ,またはその両方で指定するように促されるので,ここではファイルfを指定します。

$ rr
f/d/a/q?
> f

すると,カレントディレクトリの対象となるファイルが一覧表示されるので,たとえばappleefにする場合はe fと入力します。

ls:
  file: apple.md

Put the words before & after
> e f

置換対象の指定が済むと,次のように処理前後の状態が表示されます(1)⁠。意図のとおりであればyを入力し,そうでなければ[Enter]キーでやりなおします。

from:         
  apple.md    
to:           |(1)
  applf.md    

Change OK? [y/N]
> y
ls:
  file: applf.md
注3)
これ以降で紹介する公開プログラムについても同様です。
注4)
引数にrcopyを渡すと,同様の方法でファイルを複製することもできます。詳しくはGitHubリポジトリのREADMEを参照してください。
注5)
bashの起動時に読み込まれる設定ファイルの一つです。これに設定を記述しておくと,シェルを起動するたびに同じ内容を入力する必要がなくなります。

<続きの(2)こちら。>

WEB+DB PRESS

本誌最新号をチェック!
WEB+DB PRESS Vol.113

2019年10月24日発売
B5判/160ページ
定価(本体1,480円+税)
ISBN978-4-297-10905-9

  • 特集1
    接続エラー,性能低下,権限エラー,クラウド障害
    AWSトラブル解決
    原因調査・対応・予防のノウハウ
  • 特集2
    Ruby書き方ドリル
    要点解説と例題で身に付く!
  • 特集3
    体験
    ドメイン駆動設計
    モデリングから実装までを一気に制覇
  • 一般記事
    FigmaによるUIデザイン
    デザイナーとエンジニアがオンラインで協業できる!
  • 一般記事
    入門
    SwooleによるPHP非同期処理
    高速化のための並列実行はどのように書くのか