sed/awkよりもPerlのほうがポータブル説 (とKnot-Resovler用mackerel-pluginの話)

知らんけど。たった一回の経験に基づいた発見なので、そんなに真面目に読まなくていいヤツだ。

切っ掛けは、 Knot Resolver というDNSリゾルバ(キャッシュサーバ)デーモンのキャッシュDB利用率を mackerel.io 上でグラフ化しようと思ったことだ。

kres-cache-gc というコマンド(キャッシュのガベコレデーモン)を dry-run すると、以下のように利用率が表示されることが分かったので、それをパースする簡単な mackerel-plugin を書くことにした (*1) 。

% sudo kres-cache-gc -c /var/run/kresd -n
Knot Resolver Cache Garbage Collector, version 5.5.2
Usage: 77.42%

さて、この表示内容から Usage のパーセンテージを取り出さなければいけない。 上手くsed/awkなりでシェルスクリプトにするか、と思ったところで我に返った。そもそもsed/awkで書くのはポータブルなのだろうか。

今回の内容ならawkで一発だろうとは思ったが、awkの文法は正直あまり馴染んでいないし、ぱっと思い出せなかった。 しかも/usr/bin/awk の実態が nawk (New awk) であるか gawk (GNU awk)であるかは環境によって異なる。今回の環境は FreeBSD だので見事に nawk だ。これはsedにも当てはまり、 BSD sed と GNU sed という二つのバリアントが存在する。他ならずシェルスクリプトを使う理由がコーディングコストの低さと移植性によるものだとしたら、ざっくり半分のメリットが無くなっている。

一方正規表現なら、肯定先読み・後読みを使って以下のように書くことをすぐに思いついた。

/(?<=Usage: ).+(?=%)/

こうした複雑な正規表現ならPCREがスタンダードだ。 perl のワンライナーに流せば正しくパーセンテージを抽出できていることが分かる。

% echo "hogehoge\n Usage: 77.1%" | perl -ne 'print $& if (/(?<=Usage: ).+(?=%)/)'
77.1

そしてPerlなら大概の環境にプレインストールされている。下手すりゃbashよりも広く入っているかもしれない。 Perlそのものの可読性だとかCPANだとかはさておき、少なくともsed/awkで書くようなタスクをPerlで書くことにはまず困らない気がする。ワンライナーでシェルスクリプトと組み合わせても良いし、Perlで完結してもいい。

じゃ sed/awk より perl のほうがポータブルじゃん?って思ってツイを検索したら既に似たような意見が書かれていた。まあ自分が思いつくようなことは誰かが思いついているものだ。

とりあえずシェルスクと Perl を組み合わせて書いた mackerel-plugin-kresd_cache を貼っておく。誰かがコントロールソケットを用いたちゃんとしたヤツに書き換えてくれるまでは使えるかもしれない。

#!/bin/sh

set -ue
PATH="$PATH:/usr/local/sbin:/usr/local/bin"
metric_name="knotresolver.cache_usage.percentage"
epoch=$(date '+%s')

metric=`kres-cache-gc -c /var/run/kresd -n 2>/dev/null | perl -ne 'print $& if (/(?<=Usage: ).+(?=%)/g)'`
echo -e "${metric_name}\t${metric}\t${epoch}"

(*1) 正攻法は /var/run/kresd/control/$pid にあるソケットに接続し、キャッシュ利用率を見るコマンドを叩いてその結果をパースするのがいいだろう


graph-cache-usage

上記が mackerel.io に作成されたキャッシュ利用率のグラフになる。デフォルト設定ではキャッシュストレージの利用率が 80% を超えた瞬間にガベージコレクションが実行される様子が見て取れる。


追記 (2025-03-21)

インターネットに古くからある「cshによるプログラミングは有害だ」"Csh Programming Considered Harmful" という論において、私が述べていることと似た視点で Perl に触れられていた。ここに引用しておく :

If you don't have to use a shell, but just want an interpreted language, many other free possibilities present themselves, like Perl, REXX, TCL, Scheme, or Python. Of these, Perl is probably the most widely available on UNIX (and many other) systems and certainly comes with the most extensive UNIX interface. Some vendors even ship it standard.

If you have a problem that would ordinarily use sed or awk or sh, but it exceeds their capabilities or must run a little faster, and you don't want to write the silly thing in C, then Perl may be for you. You can get at networking functions, binary data, and most of the C library. There are also translators to turn your sed and awk scripts into Perl scripts, as well as a symbolic debugger. Tchrist's personal rule of thumb is that if it's the size that fits in a Makefile, it gets written in the Bourne shell, but anything bigger gets written in Perl.

https://www.mars.org/home/rob/docs/csh-whynot.html