シェルスクリプト中でファイルサイズをポータブルに得る方法

ファイルサイズを得たい!ってときに真っ先に思い浮かぶのは stat(1) にサイズを表示させるフォーマットを指定する方法。
だけど GNU stat -c %s $FILENAME に対して、BSD stat -f %z $FILENAME と、オプションもフォーマットも違う。


次に思いつくのは ls -l の結果を利用すること。

% ls -l $FILENAME | while read a b c d e f
do
  echo $e
done

これは GNU ls でも BSD ls でもちゃんとファイルサイズを表示する。
しかし、ファイルサイズを得たいのに ls -l を使うのはちょっと大げさな気もする。


もう少しマシな方法は wc -c を利用すること。GNU wc と BSD wc で微妙に表示の仕方が違う*1けど、問題無い。

% wc -c $FILENAME | while read a b
do
  echo $a
done

こっちのほうが ls -l より早い。1000回まわして1秒くらいの差が出る。
というか、read a b; echo $a とは書けないのかなぁ。ksh, zsh ならこれでいけるんだけど、bash だとダメ。
csh は知らね。
当然 awk '{print $1}' にしてもいいんだけど、そうするとまた処理が遅くなる。
Command-line fu でもやたらと awk '{print $N}' が登場するけど、それくらいならビルトインの read を使ったほうがいいと思うんだけどなー。
while IFS=, read a b c; とかよくやってる。
awk を使うなら、パターンマッチを含む処理*2とか、BEGIN{} と END{} を使うようなものでないと。
まぁ bash で while を使うとサブシェルの罠が待っていたりするわけだけど。


おまけ。
zsh に限定すれば、さらに良い方法がある。

% zmodload zsh/stat
% stat +size $FILENAME

stat をビルトインで持っている。ビルトインだから当然圧倒的に早い。zsh 最強すぐる!

  • ポータビリティは wc -c = ls -l >> zsh+stat >>>> stat
  • 早さは zsh+stat >>> stat > wc -c > ls -l

こんなところか。

*1:空白の使い方が違う

*2:例えば awk '/pattern/{print $1}' のような。grep + read でやるくらいなら awk のほうが俺はいいと思う