シェルで小数秒を含む間隔をフォーマットして表示する

シェルで小数秒を含む間隔をフォーマットして表示する

人間が判読できる形式 (H:MM:SS.SSS) で表示したい、小数点付きの秒数単位の間隔があります。たとえば、16633.284 は 4:37:13.284 と表示されます。これらの時間はすべて 24 時間未満ですが、24 時間を超えていても、時間単位になります。

さらにいくつか例を挙げます。

   0      → 0:00:00.000
  60.394  → 0:01:00.394
8944.77   → 2:29:04.770

10 時間未満では固定幅であることに注意してください。もちろん、 を使用すると簡単に実行できますprintf

私は自分の解決策を回答として投稿していますが、もっと良い方法があるはずだと感じるので、他の方法は何ですかと尋ねています。私は、bashisms、zshisms などの方法も受け入れます。

注: これは次の内容に関連しています秒を日/時間/分/秒として表示しますか?しかし、bash の演算は整数のみであるため、これらのアプローチはすべて機能しません。

答え1

小数部分は時間、分、秒の数値には影響しないので、単に小数部分を省いて、小数部分なしで計算し、最後に小数部分を追加することができます。次のようになります。

#!/bin/sh
seconds=16633.284
f=${seconds##*.}
if [ "$f" = "$seconds" ]; then f=0; fi
t=${seconds%.*}
s=$((t%60))
m=$((t/60%60))
h=$((t/60/60))
printf '%d:%02d:%06.3f\n' $h $m $s.$f

出力:

   0     → 0:00:00.000
  33.    → 0:00:33.000
    .21  → 0:00:00.210
  60.394 → 0:01:00.394
8944.77  → 2:29:04.770

これは、sh のようなシェル (POSIX 以前の古いシェルを除く) で動作します。

答え2

私のアプローチはこれです。dc計算には次を使用します。

seconds=16633.284
printf '%i:%02i:%06.3f\n' \
    $(dc -e "$seconds d 3600 / n [ ] n d 60 / 60 % n [ ] n 60 % f")

printf はおそらくかなり馴染みがあると思いますが、そうでない場合は、%i整数を意味します。 は、%02i整数が 1 桁の場合、先頭に 0 を埋め込むことを意味します。%06.3fは最も奇妙ですが、小数点の後に 3 桁を意味し、合計の長さが 6 (小数点を含む) 未満の場合は先頭に 0 を埋め込みます。

意図的に引用符で囲まれていない$(…)置換により、次の 3 つのパラメータが提供されますdc

まず、$seconds(拡張された、つまり実際の数値) をスタックにプッシュし、それを複製し、(切り捨てを使用して) 3600 で割って時間を取得します。それをポップして出力し、スペースをプッシュして出力します ( [ ] n)。

すると、スタックには秒だけが残ります。これを複製し、60 で割ります (ここでも切り捨て)。これで分数が得られ、60 を法として時間を切り捨てます。これをスペース付きで出力します。スタックには秒だけが残ります。

最後に、秒フィールドのみを返す mod 60 を実行します。dc では、係数は小数点に完全に適合し、精度を維持します。f次に、スタックと改行を出力します。

答え3

プレーンなshで簡単にできますGNU date を使用すると、コードを少し短くすることができます (ミリ秒を切り捨ててもかまいません。最も近い値に切り捨てると、少し長くなります)。

hours=$((${seconds%.*} / 3600))
min_s_nano=$(TZ=GMT date -d @$seconds +%M:%S.%N)
time_string=$hours:${min_s_nano%??????}

答え4

呼び出してdc秒数をスタックに配置し、計算 (secs->HH/MM/SS.MSEC)、フォーマット、結果の表示を実行します。

seconds=16633.284

dc <<DC
# rearrange stack for number < 1hr
[r ldx q]sb

# rearrange stack for number < 1min
[rd ldxq]sc

# min++, sec-=60   hrs++, min-=60
[r1+r 60-d 60!>a]sa

# display result as h:mm:ss.sss
[n 58an 2lpx 58an 2lpx 46an 3lpx 10an]sd

# perform the conversion of seconds -> hours, minutes, seconds
[d60 >c lax r0rd60 >b lax r ldx]si

[
d1000* 1000% 1/r   # fractional portion
 1000* 1000/ 1/0r  # integer    portion
lix
]sh

# left zero-pad a number
[lk1+d sk 0n le >g ]sg
[sedZd sk    le >gn]sp

# setup the number on the stack and begin computation
$seconds lhx
DC

結果

0                 -->  0:00:00.000
60.394            -->  0:01:00.394
8944.77           -->  2:29:04.770
86399.99          -->  23:59:59.990
59.9999           -->  0:00:59.999
599.9999          -->  0:09:59.999
16633.284         -->  4:37:13.284
33                -->  0:00:33.000
.21               -->  0:00:00.210
123456789.123456  -->  34293:33:09.123

関連情報