数字を 123k とか 123.45M とか、大きさに応じて読みやすい(human-readableな)単位で変換する関数。
ノーマル実装
convert_unit() {
local RESULT=$1
if [[ `echo -n ${RESULT%%.*}` -lt 1000 ]]; then
echo -n "${RESULT}"
return
fi
for U in k M G T P; do
RESULT=`echo "scale=2; ${RESULT} / 1000" | bc`
[[ `echo -n ${RESULT%%.*}` -lt 1000 ]] && break
done
printf "%.2f%s" "${RESULT}" ${U:-P}
}
普通にループで1000で割り続ける方法。
とりあえず、ペタまで対応しました。
実行結果
while read VAL; do
echo "$VAL => `convert_unit $VAL`"
done < <(cat <<!!!
1
999
4321
7654321
1987654321
4321987654321
7654321987654321
1987654321987654321
!!!
)
# 1 => 1
# 999 => 999
# 4321 => 4.32k
# 7654321 => 7.65M
# 1987654321 => 1.98G
# 4321987654321 => 4.32T
# 7654321987654321 => 7.65P
# 1987654321987654321 => 1987.65P
シェル芸的実装
と、ここまで書いてシェル芸的な実装を思いついた。
convert_unit() {
local TMP=`mktemp -u`
truncate -s "$1" $TMP
ls -lh --si $TMP | awk '{print $5}'
[[ -f $TMP ]] && rm -f $TMP
}
指定されたサイズのスパースファイルを作成して、lsコマンドに出力させる方法。
ちょっとトリッキーですが。
実行結果
1 => 1
999 => 999
4321 => 4.4k
7654321 => 7.7M
1987654321 => 2.0G
4321987654321 => 4.4T
7654321987654321 => 7.7P
1987654321987654321 => 2.0E
ls の場合は小数点以下を切り上げにするようなので、出力が若干異なります。
mktemp コマンドで非推奨の -u
オプションを使用しているので、厳密にいえば名前が競合する可能性があるので、こちらはあくまでネタとしてどうぞ。