シェルスクリプトで値を並べて出力したのに、桁がそろわずに読みにくい表ができあがる。
そんな出力を echo のまま整えようとして、空白を手で数えた経験はないでしょうか。
この手の整形こそ、printf の出番。
printfは「何を出すか」と「どう並べるか」を分けて書けるコマンドで、C言語の同名関数とほぼ同じ書式を使います。
この記事の出力はすべて手元で実行して確かめたもので、環境は GNU bash 5.2.37 と GNU coreutils 8.32 です。
echoとの違いは「改行」と「解釈」の2点
まず押さえたいのが、echo との振る舞いの違い。
printfは改行を自動で付けない
echo は文字列の末尾に改行を足しますが、printfは書式に書いた分しか出力しません。
$ printf 'x'; printf 'y'; printf '\n'
xy
改行が要るなら、書式の末尾に \n を自分で書く決まり。
面倒に見えて、これは途中経過を1行に書き足していく用途でそのまま効いてきます。
echoのエスケープ解釈は環境で揺れる
もうひとつの違いが、\t のようなエスケープをどう扱うかという点です。
$ /bin/echo "a\tb"
a\tb
$ /bin/echo -e "a\tb"
a b
$ printf 'a\tb\n'
a b
echo は -e を付けて初めてタブとして解釈しましたが、この -e の要否はシェルや実装によって変わります。
bash の組み込み echo と /bin/echo でも挙動が分かれるため、スクリプトの移植で足をすくわれやすいところ。
printf は最初からエスケープを解釈する仕様なので、オプションに依存せず同じ結果になります。
移植性を気にする場面で printf が勧められるのは、この一貫性が理由でした。
書式指定子で桁と型をそろえる
printf の本領は、第1引数に置く書式文字列にあります。
%s・%d・%fの使い分け
よく使う指定子は3つで、%sが文字列、%dが整数、%fが小数。
小数は %.2f のように桁数を書くと、その位で丸めて出力されます。
$ printf '%.2f\n' 3.14159
3.14
$ printf '%08.3f\n' 3.14159
0003.142
2つ目の %08.3f は「全体8文字・小数3桁・空きはゼロ埋め」という指定。
連番のファイル名を作るときなど、ゼロ埋めが一発で書けるのは地味に助かります。
連番そのものを作る側は seq の担当で、使い方は別記事にまとめました。
幅と寄せを指定すれば表になる
指定子の前に数字を書くとその幅を確保し、マイナスを付ければ左寄せ。
$ printf '%-10s|%5s|\n' name qty
name | qty|
$ printf '%-10s|%5d|\n' apple 3
apple | 3|
$ printf '%-10s|%5d|\n' banana 12
banana | 12|
空白を手で数える作業から解放されるのが、この幅指定のありがたいところ。
ただしこの桁ぞろえが効くのは半角文字だけで、日本語のような全角文字が混ざると見た目の幅はずれてしまいます。
printf が数えているのは表示上の幅ではなく文字数なので、そこは割り切りが要りました。
引数が余ると書式が繰り返される
printf には、知らないと驚く仕様がひとつあります。
書式の指定子より引数が多いと、書式が先頭から繰り返し適用されるという挙動です。
$ printf '%s-%s\n' a b c d e
a-b
c-d
e-
5個の引数に対して書式が3回まわり、足りなかった最後の %s は空文字として扱われました。
この性質を使えば、配列やコマンド出力をまとめて同じ書式へ流し込めます。
逆に、引数の個数が想定とずれたときは黙って行が増えるので、ループの代わりに使うなら個数の管理が前提でした。
変数を書式文字列そのものに置かない
もうひとつ覚えておきたいのが、変わる値を書式の側に混ぜないという原則。
# 変数の中の % が書式として解釈されてしまう
$ printf "$msg"
# 第2引数として渡せば安全
$ printf '%s\n' "$msg"
ユーザー入力やファイル名が % を含んでいると、前者は意図しない解釈で壊れます。
書式はリテラルで固定し、変わる値は引数として渡す。
この一点を守るだけで、printf まわりの事故はほとんど防げるはずです。
なお bash の printf には %q という指定子もあり、シェルへ渡し直しても壊れない形に引用符を付けてくれます。
$ printf '%q\n' 'a b*c'
a\ b\*c
空白やアスタリスクを含むパスを組み立てる場面なら、エスケープの取りこぼしを機械にまかせられるわけです。
書式の細かい規定は GNU Coreutils の printf のマニュアル にまとまっているので、迷ったら一次情報を当たってください。
最後に
printf は、改行を自分で書く・書式で桁をそろえる・変わる値は書式の外から渡す、この3点を押さえれば十分に使えます。
echo との違いも、エスケープ解釈が環境に左右されないという一点で腑に落ちました。
出力の見た目をコードで管理できるようになると、スクリプトの読みやすさが変わってきます。
次にシェルで表を組みたくなったら、空白を数える前に printf を思い出してみてください。
同じ「書式を先に決めて値を流し込む」考え方は、日付を扱う date コマンドでもそのまま効いてきます。
以上です。










コメントを残す