쉘에서 소수 초 단위로 간격 형식 지정 및 표시

쉘에서 소수 초 단위로 간격 형식 지정 및 표시

사람이 읽을 수 있는(H:MM:SS.SSS) 형식으로 표시하고 싶은 간격(초 단위)이 있습니다. 예를 들어 16633.284는 4:37:13.284로 표시되어야 합니다. 이 모든 시간은 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한 자리 숫자인 경우 앞에 0을 붙여 정수를 채우는 것을 의미합니다. %06.3f가장 이상한 것이지만 소수점 이하 3자리를 의미하며, 전체 길이가 6(소수점 포함) 미만인 경우 앞에 0을 붙인다는 의미입니다.

의도적으로 인용되지 않은 $(…)대체는 다음을 사용하여 세 가지 매개변수를 제공합니다 dc.

먼저 $seconds(확장되어 실제 숫자) 스택에 푸시합니다. 그것을 복제하십시오; 그런 다음 (잘라내서) 3600으로 나누어 시간을 얻습니다. 그것을 팝하고 인쇄한 다음 공백을 누르고 인쇄하십시오( [ ] n).

그러면 스택에 몇 초만 남게 됩니다. 그것을 복제하고 60으로 나눕니다(다시 잘림). 몇 분이 걸립니다. 시간을 낭비하려면 모드 60을 사용하세요. 공백을 넣어 다시 인쇄하세요. 스택에 초만 남겨 둡니다.

마지막으로 초 필드만 제공하는 mod 60과 dc에서 모듈러스는 소수점 이하 자릿수에 완벽하게 만족하고 정밀도를 유지합니다. f그런 다음 스택과 개행 문자를 인쇄합니다.

답변3

일반 sh에서 쉽게 할 수 있습니다. GNU 날짜를 사용하면 코드를 약간 더 짧게 만들 수 있습니다(밀리초 단위로 반올림해도 괜찮기를 바랍니다. 가장 가까운 단위로 반올림하면 약간 길어집니다).

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

답변4

호출하여 dc스택에 초 수를 배치하고 계산(초->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

관련 정보