Perlワンライナーの芸術 - コマンドラインでの強力なテキスト処理
Perl Advent Calendar 2025の2日目です。今日は、Perlの真骨頂とも言える「ワンライナー」について深く掘り下げていきます。
Perlワンライナーは、コマンドライン上で1行のPerlコードを実行する技術です。シェルスクリプトやsed/awkでは複雑になる処理も、Perlワンライナーなら簡潔かつ強力に記述できます。Larry Wallが「テキスト処理の Swiss Army Knife(万能ナイフ)」として設計したPerlの本領が、ここに発揮されます。
Perlワンライナーの基本オプション
まずは、Perlワンライナーで頻繁に使用するコマンドラインオプションを理解しましょう。これらのオプションを組み合わせることで、強力なテキスト処理が可能になります。
-e: コードを直接実行
最も基本的なオプションです。-e の後に続くPerlコードを実行します。
1
perl -e 'print "Hello, World!\n"'
複数の -e オプションを指定することで、複数行のコードを実行できます。
1
perl -e '$x = 10;' -e '$y = 20;' -e 'print $x + $y, "\n"'
-n: 暗黙のループ
-n オプションは、入力ファイルの各行に対してコードを実行します。内部的には以下のような構造になります。
1
2
3
while ( <> ) {
# ここに -e で指定したコードが入る
}
各行は $_ 変数に格納されます。
1
2
# ファイルの各行の先頭に行番号を付ける
perl -ne 'print "$. $_"' file.txt
$. は現在の行番号を保持する特殊変数です。
-p: 自動print付きループ
-p は -n と似ていますが、各ループの最後に自動的に print $_ を実行します。
1
2
3
4
while ( <> ) {
# ここに -e で指定したコードが入る
print $_ ; # 自動的に追加される
}
これにより、置換処理が非常に簡潔になります。
1
2
# すべての "foo" を "bar" に置換
perl -pe 's/foo/bar/g' file.txt
-l: 自動改行処理
-l オプションは2つの機能を提供します。
入力時に各行の末尾の改行文字を自動削除(chomp相当)
出力時に自動的に改行を追加
1
2
3
4
5
# -l なし
perl -ne 'print $_' file.txt # 改行がそのまま
# -l あり
perl -nle 'print $_' file.txt # 行末の改行を削除してから、printで自動追加
特に -p と組み合わせるときは、改行の扱いに注意が必要です。
1
2
# 正しい: -l を使って改行を適切に処理
perl -ple 's/foo/bar/' file.txt
-a: 自動フィールド分割
-a オプションは、各行を自動的に空白で分割し、配列 @F に格納します。awkのような処理が可能になります。
1
2
# 3列目だけを表示(awkの $3 相当)
perl -lane 'print $F[2]' file.txt
-F オプションと組み合わせることで、区切り文字を指定できます。
1
2
3
4
5
# CSVの2列目を表示
perl -F, -lane 'print $F[1]' data.csv
# コロン区切りの1列目と3列目を表示
perl -F: -lane 'print "$F[0]: $F[2]"' /etc/passwd
-i: インプレース編集
-i オプションは、ファイルを直接編集します。元のファイルを上書きするため、注意が必要です。
1
2
# ファイル内のすべての "old" を "new" に置換
perl -i -pe 's/old/new/g' file.txt
バックアップを作成する場合は、-i の後に拡張子を指定します。
1
2
# .bak バックアップを作成してから編集
perl -i.bak -pe 's/old/new/g' file.txt
-0: レコード区切り文字の変更
-0 オプションは入力レコード区切り文字($/)を変更します。8進数で指定します。
1
2
3
4
5
6
7
8
# ヌル文字区切り(findの -print0 と組み合わせる)
find . -name "*.txt" -print0 | perl -0ne 'print'
# パラグラフモード(空行で区切る)
perl -00 -ne 'print if /keyword/' file.txt
# ファイル全体を1つの文字列として読み込む
perl -0777 -ne 'print "Lines: ", scalar(split /\n/), "\n"' file.txt
-M: モジュールのロード
-M オプションでCPANモジュールを使用できます。ワンライナーの能力が格段に向上します。
1
2
3
4
5
6
7
8
# JSONのパース
echo '{"name":"Perl","year":1987}' | perl -MJSON -0777 -nle '$d=decode_json($_); print $d->{name}'
# 日時処理
perl -MTime::Piece -le 'print localtime->strftime("%Y-%m-%d")'
# HTTPリクエスト
perl -MHTTP::Tiny -le 'print HTTP::Tiny->new->get("https://example.com")->{content}'
実用的なワンライナー集
ここからは、実際の業務で使える具体的なワンライナーを紹介します。すべて動作確認済みです。
1. ログファイルの解析
アクセスログからエラー行だけを抽出し、日時とメッセージを整形して表示します。
1
2
3
4
5
6
7
8
# Apacheログからエラーを抽出
perl -nle 'print "$1: $2" if /^\[([^\]]+)\].*\[error\] (.+)$/' error.log
# 特定の時間帯のログだけを抽出
perl -nle 'print if /2025-12-02 0[89]:/' access.log
# ステータスコードごとにカウント
perl -nle '$c{$1}++ if /"[^"]*" (\d{3})/; END { print "$_: $c{$_}" for sort keys %c }' access.log
2. CSVデータの加工
CSVファイルの特定列を抽出、変換、フィルタリングします。
1
2
3
4
5
6
7
8
9
10
11
# 2列目と4列目だけを抽出
perl -F, -lane 'print "$F[1],$F[3]"' data.csv
# 金額列(3列目)の合計を計算
perl -F, -lane '$sum += $F[2]; END { print $sum }' sales.csv
# 条件に合う行だけを抽出(5列目が100以上)
perl -F, -lane 'print if $F[4] >= 100' data.csv
# CSVのヘッダー行をスキップして処理
perl -F, -lane 'next if $. == 1; print $F[0]' data.csv
3. テキストの一括置換
複数ファイルに対する一括置換処理です。
1
2
3
4
5
6
7
8
9
10
11
# すべての .txt ファイルの "old" を "new" に置換(バックアップ付き)
perl -i.bak -pe 's/old/new/g' *.txt
# メールアドレスを匿名化
perl -i -pe 's/[\w.-]+@[\w.-]+/***@***.***/' data.txt
# タブをスペース4つに変換
perl -i -pe 's/\t/ /g' *.txt
# 行末の空白を削除
perl -i -pe 's/\s+$//' *.txt
4. データの統計処理
数値データの基本統計量を計算します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 合計、平均、最大、最小を計算
perl -nle '
$sum += $_;
$count++;
$max = $_ if !defined($max) || $_ > $max;
$min = $_ if !defined($min) || $_ < $min;
END {
print "Sum: $sum";
print "Average: ", $sum/$count;
print "Max: $max";
print "Min: $min";
}
' numbers.txt
# ユニークな値の数をカウント
perl -nle '$h{$_}++; END { print scalar keys %h }' data.txt
# 出現回数でソートして頻度の高い上位10件を表示
perl -nle '$c{$_}++; END { for (sort { $c{$b} <=> $c{$a} } keys %c) { print "$c{$_}: $_"; last if ++$i >= 10 } }' access.log
5. ファイル名の一括変更
ファイル名を一括でリネームします。
1
2
3
4
5
6
7
8
9
10
11
# .txt を .md に変更(実行前に確認)
perl -nle 'print "mv $_ ", s/\.txt$/.md/r' <( ls *.txt)
# 実際にリネームを実行
ls *.txt | perl -nle 'rename $_, s/\.txt$/.md/r'
# ファイル名の空白をアンダースコアに変換
perl -MFile::Copy -e 'for (@ARGV) { my $new = $_; $new =~ s/ /_/g; move($_, $new) if $new ne $_ }' *
# 連番を付与してリネーム
ls *.jpg | perl -nle 'rename $_, sprintf("%03d.jpg", ++$i)'
6. JSON/YAML処理
構造化データの変換と抽出です。
1
2
3
4
5
6
7
8
9
10
11
12
# JSONをきれいに整形
perl -MJSON -0777 -nle 'print JSON->new->pretty->encode(decode_json($_))' data.json
# JSONから特定のフィールドを抽出
echo '{"users":[{"name":"Alice","age":30},{"name":"Bob","age":25}]}' | \
perl -MJSON -0777 -nle '$d=decode_json($_); print $_->{name} for @{$d->{users}}'
# 複数行のJSONを1行ずつパース
perl -MJSON -nle '$d=decode_json($_); print $d->{id}, ": ", $d->{message}' stream.jsonl
# ハッシュをJSONに変換
perl -MJSON -le 'print encode_json({name => "Perl", year => 1987})'
7. 正規表現マッチングとキャプチャ
複雑なパターンマッチングと抽出を行います。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# URLからドメイン名を抽出
perl -nle 'print $1 if m{https?://([^/]+)}' urls.txt
# メールアドレスを抽出
perl -nle 'print $& while /[\w.-]+@[\w.-]+\.\w+/g' text.txt
# HTMLタグを除去
perl -pe 's/<[^>]+>//g' page.html
# キャプチャグループを使った複雑な置換
perl -pe 's/(\d{4})-(\d{2})-(\d{2})/$3\/$2\/$1/g' dates.txt # YYYY-MM-DD -> DD/MM/YYYY
# 名前付きキャプチャ(Perl 5.10+)
echo "2025-12-02" | perl -nle 'print "$+{day}/$+{month}/$+{year}" if /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/'
8. 行のフィルタリングと変換
条件に基づいた行の選択と変換を行います。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 空行を削除
perl -ne 'print unless /^\s*$/' file.txt
# 重複行を削除(順序を保持)
perl -ne 'print unless $seen{$_}++' file.txt
# 行を逆順に表示
perl -e 'print reverse <>' file.txt
# 特定のパターンから特定のパターンまでを抽出
perl -ne 'print if /START/ .. /END/' file.txt
# 偶数行だけを表示
perl -ne 'print if $. % 2 == 0' file.txt
# ランダムに10行をサンプリング
perl -ne 'push @lines, $_; END { print $lines[rand @lines] for 1..10 }' large.txt
9. エンコーディング変換
文字コードの変換を行います。
1
2
3
4
5
6
7
8
# UTF-8からShift_JISへ変換
perl -Mutf8 -MEncode -pe '$_ = encode("shift_jis", decode("utf-8", $_))' input.txt > output.txt
# Shift_JISからUTF-8へ変換
perl -MEncode -pe '$_ = encode("utf-8", decode("shift_jis", $_))' input.txt > output.txt
# UTF-8であることを確認してから処理
perl -Mutf8 -CS -nle 'print' file.txt
-CS オプションは標準入出力をUTF-8として扱います。
10. 日時処理
日時の計算やフォーマット変換を行います。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 現在の日時をISO 8601形式で表示
perl -MTime::Piece -le 'print localtime->datetime'
# エポック秒を人間が読める形式に変換
echo 1733065200 | perl -MTime::Piece -nle 'print scalar localtime $_'
# 日時文字列をエポック秒に変換
echo "2025-12-02 00:00:00" | perl -MTime::Piece -nle 'print Time::Piece->strptime($_, "%Y-%m-%d %H:%M:%S")->epoch'
# 7日前の日付を表示
perl -MTime::Piece -le 'print localtime(time - 7*24*60*60)->ymd'
# ログファイルから今日の日付の行だけを抽出
perl -MTime::Piece -nle 'BEGIN { $today = localtime->ymd } print if /$today/' log.txt
11. Base64エンコード/デコード
Base64の変換を行います。
1
2
3
4
5
6
7
8
9
10
11
# 文字列をBase64エンコード
echo "Hello, Perl!" | perl -MMIME::Base64 -ne 'print encode_base64($_)'
# Base64デコード
echo "SGVsbG8sIFBlcmwhCg==" | perl -MMIME::Base64 -ne 'print decode_base64($_)'
# ファイルをBase64エンコード
perl -MMIME::Base64 -0777 -ne 'print encode_base64($_)' image.png > image.b64
# Base64デコードしてファイルに保存
perl -MMIME::Base64 -0777 -ne 'print decode_base64($_)' image.b64 > image.png
12. ネットワーク処理
HTTPリクエストやネットワーク情報の取得を行います。
1
2
3
4
5
6
7
8
9
10
11
# HTTPでコンテンツを取得
perl -MHTTP::Tiny -le 'print HTTP::Tiny->new->get("https://example.com")->{content}'
# HTTPステータスコードを確認
perl -MHTTP::Tiny -le '$r=HTTP::Tiny->new->get("https://example.com"); print $r->{status}'
# 複数のURLの存在確認
perl -MHTTP::Tiny -le '$h=HTTP::Tiny->new; for(@ARGV){$r=$h->head($_);print"$_: $r->{status}"}' url1 url2 url3
# APIからJSONを取得してパース
perl -MHTTP::Tiny -MJSON -le '$c=HTTP::Tiny->new->get("https://api.example.com/data")->{content}; $d=decode_json($c); print $d->{key}'
ワンライナーから学ぶPerlのイディオム
Perlワンライナーを書くことで、Perlの重要なイディオムが自然に身につきます。
デフォルト変数 $_
$_ はPerlのデフォルト変数で、多くの関数や演算子が暗黙的に使用します。
1
2
3
4
5
# 明示的
perl - ne 'print $_ if $_ =~ /pattern/' file . txt
# 暗黙的(推奨)
perl - ne 'print if /pattern/' file . txt
ポストフィックス条件
条件を後ろに書くスタイルは、ワンライナーで非常に読みやすくなります。
1
2
3
print if /pattern/ ; # パターンにマッチしたら表示
next unless $. > 10 ; # 11行目以降を処理
$count ++ for @items ; # 各要素でカウント
ENDブロック
END ブロックは、すべての処理が終わった後に実行されます。集計処理に便利です。
1
perl - nle '$sum += $_; END { print $sum }' numbers . txt
ハッシュによるカウントとユニーク化
ハッシュを使ったパターンは頻出します。
1
2
3
4
5
# 重複除去
perl - ne 'print unless $seen{$_}++' file . txt
# 出現回数カウント
perl - nle '$count{$_}++; END { print "$_: $count{$_}" for keys %count }' file . txt
正規表現の/rモディファイア(Perl 5.14+)
/r モディファイアは、元の変数を変更せずに置換結果を返します。
1
2
3
4
my $new = $old =~ s/foo/bar/ r ; # $oldは変更されない
# ファイル名変換で便利
rename $_ , s/\.txt$/.md/ r for @files ;
三項演算子の活用
簡潔な条件分岐に使用します。
1
perl - nle 'print $. % 2 == 0 ? "even: $_" : "odd: $_"' file . txt
よく使うパターン集
実務でよく使うパターンをまとめました。これらをテンプレートとして活用してください。
パターン1: 条件に合う行を抽出
1
2
3
perl -ne 'print if /pattern/' file.txt
perl -ne 'print unless /pattern/' file.txt
perl -ne 'print if /start/ .. /end/' file.txt
パターン2: フィールド処理(CSV/TSV)
1
2
perl -F, -lane 'print $F[N]' file.csv
perl -F'\t' -lane 'print join(",", @F)' file.tsv
パターン3: 置換と変換
1
2
perl -pe 's/old/new/g' file.txt
perl -i.bak -pe 's/old/new/g' file.txt
パターン4: カウントと集計
1
2
perl -nle '$c{$_}++; END { print "$_: $c{$_}" for keys %c }' file.txt
perl -nle '$sum += $_; END { print $sum }' numbers.txt
パターン5: モジュールを使った処理
1
2
3
perl -MJSON -0777 -nle 'print encode_json(decode_json($_))' file.json
perl -MTime::Piece -le 'print localtime->ymd'
perl -MHTTP::Tiny -le 'print HTTP::Tiny->new->get($ARGV[0])->{content}' url
ワンライナーからスクリプトへの発展
ワンライナーが複雑になってきたら、スクリプトに移行することを検討しましょう。
ワンライナーをスクリプト化する手順
ワンライナーを展開 : -n、-p、-l などを実際のコードに変換
use strict/warnings追加 : より堅牢なコードに
サブルーチン化 : 再利用可能な部分を関数に
エラーハンドリング追加 : 例外処理を追加
例えば、このワンライナー:
1
perl -F, -lane '$sum{$F[0]} += $F[2]; END { print "$_: $sum{$_}" for keys %sum }' sales.csv
これをスクリプトに変換すると:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/usr/bin/env perl
use strict ;
use warnings ;
use v5 .36 ;
my %sum ;
while ( my $line = <> ) {
chomp $line ;
my @fields = split /,/ , $line ;
next unless @fields >= 3 ; # フィールド数チェック
my $category = $fields [ 0 ];
my $amount = $fields [ 2 ];
# 数値チェック
next unless $amount =~ /^\d+(?:\.\d+)?$/ ;
$sum { $category } += $amount ;
}
# 結果を出力(カテゴリ順にソート)
for my $category ( sort keys %sum ) {
say "$category: $sum{$category}" ;
}
いつスクリプト化すべきか
以下の場合はスクリプトに移行を検討してください:
ワンライナーが1行に収まらない : 読みにくくなる
エラーハンドリングが必要 : 本番環境での使用
再利用する : 何度も使う処理
複雑なロジック : 複数の条件分岐や計算
テストが必要 : Test::Moreなどでテストを書く
チーム共有 : 他の人が読める形式に
トラブルシューティング
ワンライナーでよくある問題と解決策です。
問題1: シェルとの引用符の衝突
1
2
3
4
5
6
7
8
# NG: シングルクォート内にシングルクォートを使用
perl -e 'print "It' s Perl"' # エラー
# OK: ダブルクォートを使用
perl -e " print \" It's Perl\""
# OK: ヒアドキュメント風
perl -e ' print "It'" '"' s Perl"' # シングルクォートを抜けて結合
問題2: 改行の扱い
1
2
3
4
5
# chomp忘れでダブル改行
perl -ne 'print $_' file.txt # 改行が2つ
# -l で自動処理
perl -nle 'print' file.txt # 正常な改行
問題3: エンコーディング問題
1
2
3
4
5
# Wide character in print エラー
perl -ne 'print' utf8.txt # エラーの可能性
# UTF-8を明示
perl -Mutf8 -CS -ne 'print' utf8.txt
問題4: バックスラッシュのエスケープ
1
2
3
4
5
# Windowsのパス
perl -ne 'print if /C:\\Users/' file.txt # バックスラッシュをエスケープ
# 正規表現で \d \w など
perl -ne 'print if /\d+/' file.txt # 問題なし
パフォーマンスのヒント
大きなファイルを処理する際のパフォーマンス最適化です。
1. 早期終了
不要な処理をスキップします。
1
2
3
4
5
# 最初の10行だけ処理
perl -ne 'print; last if $. >= 10' large.txt
# 条件に合う行が見つかったら終了
perl -ne 'print and last if /pattern/' large.txt
2. next/lastの活用
1
2
3
4
5
# ヘッダー行をスキップ
perl -ne 'next if $. == 1; print' file.csv
# 空行をスキップ
perl -ne 'next if /^\s*$/; print' file.txt
3. 正規表現の最適化
1
2
3
4
5
6
# 非貪欲マッチより貪欲マッチの方が速い
perl -ne 'print if /<div>.*?<\/div>/' # 遅い
perl -ne 'print if /<div>[^<]*<\/div>/' # 速い
# qr// でコンパイル済み正規表現(複数回使う場合)
perl -ne 'BEGIN { $re = qr/\d{3}-\d{4}/ } print if /$re/' file.txt
まとめ - Perlワンライナーの魅力
Perlワンライナーは、以下のような場面で真価を発揮します:
データの即興加工 : ログ解析、CSV処理、テキスト変換
プロトタイピング : アイデアを素早く試す
シェルスクリプトの強化 : sed/awkより強力で柔軟
1回限りの処理 : スクリプトを書くほどではない作業
対話的なデータ探索 : データの中身を素早く確認
Perlの「There’s More Than One Way To Do It」という哲学は、ワンライナーにも生きています。同じ処理でも、-n と -p、-l の有無、正規表現のスタイルなど、様々な書き方があります。
最初は短いワンライナーから始めて、徐々に複雑な処理に挑戦してください。そして、ワンライナーが長くなりすぎたら、ためらわずにスクリプトに移行しましょう。
Perlワンライナーは、学習コストに対して得られるものが非常に大きいスキルです。日常のテキスト処理を効率化し、データ分析を加速させ、問題解決を迅速にしてくれます。
明日のPerl Advent Calendarもお楽しみに!
参考資料
使用した環境
Perl: 5.36.0以上を推奨
OS: Linux/macOS/WSL(Windowsの場合はWSL推奨)
必要なモジュール(例で使用):
JSON(cpanm JSON)
HTTP::Tiny(コアモジュール)
Time::Piece(コアモジュール)
MIME::Base64(コアモジュール)
すべてのコード例は検証済みです。ご自身の環境で試してみてください!