[bash] bash 스크립트에서 경과 된 시간을 계산하는 방법은 무엇입니까?

를 사용하여 시작 및 종료 시간을 인쇄하면 date +"%T"다음과 같은 결과가 나타납니다.

10:33:56
10:36:10

이 두 가지의 차이점을 어떻게 계산하고 인쇄 할 수 있습니까?

나는 다음과 같은 것을 얻고 싶다 :

2m 14s



답변

Bash에는 SECONDS셸이 시작된 후 경과 한 시간 (초)을 추적 하는 편리한 내장 변수가 있습니다. 이 변수는 할당 될 때 속성을 유지하며 할당 후 반환 된 값은 할당 이후의 시간 (초)에 할당 된 값을 더한 값입니다.

따라서 SECONDS시간이 지정된 이벤트를 시작하기 전에 0으로 설정 하고 이벤트를 읽은 SECONDS후 표시하기 전에 시간을 산술 할 수 있습니다.

SECONDS=0
# do some work
duration=$SECONDS
echo "$(($duration / 60)) minutes and $(($duration % 60)) seconds elapsed."

이 솔루션은 date +%sGNU 확장 에 의존하지 않기 때문에 Bash가 지원하는 모든 시스템에 이식 가능합니다.


답변

경과 시간 (초)을 측정하려면 다음이 필요합니다.

  • 경과 된 초 수를 나타내는 정수
  • 이러한 정수를 사용 가능한 형식으로 변환하는 방법.

경과 된 초의 정수 값 :

  • 경과 된 초 수의 정수 값을 찾는 두 가지 bash 내부 방법이 있습니다.

    1. 배쉬 변수 SECONDS (SECONDS가 설정되어 있지 않으면 특수 속성이 손실 됨)

      • SECONDS 값을 0으로 설정 :

        SECONDS=0
        sleep 1  # Process to execute
        elapsedseconds=$SECONDS
      • SECONDS시작시 변수 값 저장 :

        a=$SECONDS
        sleep 1  # Process to execute
        elapsedseconds=$(( SECONDS - a ))
    2. Bash printf 옵션 %(datefmt)T:

      a="$(TZ=UTC0 printf '%(%s)T\n' '-1')"    ### `-1`  is the current time
      sleep 1                                  ### Process to execute
      elapsedseconds=$(( $(TZ=UTC0 printf '%(%s)T\n' '-1') - a ))

이러한 정수를 사용 가능한 형식으로 변환

bash internal printf은 직접 수행 할 수 있습니다.

$ TZ=UTC0 printf '%(%H:%M:%S)T\n' 12345
03:25:45

비슷하게

$ elapsedseconds=$((12*60+34))
$ TZ=UTC0 printf '%(%H:%M:%S)T\n' "$elapsedseconds"
00:12:34

그러나 실제로는 실제 시간이 아닌 벽시계 시간을 인쇄하므로 24 시간 이상 지속되지 않습니다.

$ hours=30;mins=12;secs=24
$ elapsedseconds=$(( ((($hours*60)+$mins)*60)+$secs ))
$ TZ=UTC0 printf '%(%H:%M:%S)T\n' "$elapsedseconds"
06:12:24

bash-hackers.org 에서 세부 사항을 좋아하는 사람들을 위해 :

%(FORMAT)TFORMAT을 형식 문자열로 사용하여 생성 된 날짜-시간 문자열을 출력합니다 strftime(3). 연관된 인수는 Epoch 이후의 시간 (초 ) 또는 -1 (현재 시간) 또는 -2 (쉘 시작 시간)입니다. 해당 인수가 제공되지 않으면 현재 시간이 기본값으로 사용됩니다.

따라서 또 다른 지속 시간 인쇄 구현이 textifyDuration $elpasedseconds있는 곳을 호출하고 싶을 수도 있습니다 textifyDuration.

textifyDuration() {
   local duration=$1
   local shiff=$duration
   local secs=$((shiff % 60));  shiff=$((shiff / 60));
   local mins=$((shiff % 60));  shiff=$((shiff / 60));
   local hours=$shiff
   local splur; if [ $secs  -eq 1 ]; then splur=''; else splur='s'; fi
   local mplur; if [ $mins  -eq 1 ]; then mplur=''; else mplur='s'; fi
   local hplur; if [ $hours -eq 1 ]; then hplur=''; else hplur='s'; fi
   if [[ $hours -gt 0 ]]; then
      txt="$hours hour$hplur, $mins minute$mplur, $secs second$splur"
   elif [[ $mins -gt 0 ]]; then
      txt="$mins minute$mplur, $secs second$splur"
   else
      txt="$secs second$splur"
   fi
   echo "$txt (from $duration seconds)"
}

GNU 날짜.

형식화 된 시간을 얻으려면 몇 가지 방법으로 외부 도구 (GNU 날짜)를 사용하여 거의 1 년 길이까지 나노초를 포함해야합니다.

날짜 안에 수학.

외부 산술이 필요하지 않습니다. 한 번에 한 단계 만 수행하십시오 date.

date -u -d "0 $FinalDate seconds - $StartDate seconds" +"%H:%M:%S"

예, 0명령 문자열에 0이 있습니다. 필요합니다.

즉, date +"%T"명령을 명령으로 변경 date +"%s"하여 값을 초 단위로 저장 (인쇄) 할 수 있다고 가정합니다 .

명령은 다음으로 제한됩니다.

  • $StartDate$FinalDate초의 양수 값
  • 의 값 $FinalDate이 (보다 늦음)보다 큽니다 $StartDate.
  • 24 시간보다 작은 시차.
  • 시간, 분 및 초가있는 출력 형식을 승인합니다. 변경하기 매우 쉽습니다.
  • -u UTC 시간을 사용할 수 있습니다. “DST”및 현지 시간 수정을 피합니다.

문자열 을 사용해야 하는 경우 10:33:56초로 변환하면 초라
는 단어가 sec로 축약 될 수 있습니다.

string1="10:33:56"
string2="10:36:10"
StartDate=$(date -u -d "$string1" +"%s")
FinalDate=$(date -u -d "$string2" +"%s")
date -u -d "0 $FinalDate sec - $StartDate sec" +"%H:%M:%S"

위에서 설명한 초 시간 변환은 “오늘”오늘의 시작 (오늘)과 관련이 있습니다.


이 개념은 다음과 같이 나노초로 확장 될 수 있습니다.

string1="10:33:56.5400022"
string2="10:36:10.8800056"
StartDate=$(date -u -d "$string1" +"%s.%N")
FinalDate=$(date -u -d "$string2" +"%s.%N")
date -u -d "0 $FinalDate sec - $StartDate sec" +"%H:%M:%S.%N"

더 긴 (최대 364 일) 시간 차이를 계산해야하는 경우 (일부) 연도를 참조로 사용하고 형식 값 %j( 연도의 일수)을 사용해야합니다 .

비슷하다:

string1="+10 days 10:33:56.5400022"
string2="+35 days 10:36:10.8800056"
StartDate=$(date -u -d "2000/1/1 $string1" +"%s.%N")
FinalDate=$(date -u -d "2000/1/1 $string2" +"%s.%N")
date -u -d "2000/1/1 $FinalDate sec - $StartDate sec" +"%j days %H:%M:%S.%N"

Output:
026 days 00:02:14.340003400

안타깝게도이 경우 1일 수에서 ONE 을 수동으로 빼야 합니다. 날짜 명령은 연중 첫날을 1로 간주합니다. 그렇게 어렵지 않습니다 …

a=( $(date -u -d "2000/1/1 $FinalDate sec - $StartDate sec" +"%j days %H:%M:%S.%N") )
a[0]=$((10#${a[0]}-1)); echo "${a[@]}"

https://www.gnu.org/software/coreutils/manual/html_node/Examples-of-date.html#Examples-of-date
는 긴 초 단위의 사용이 유효하고 문서화되어 있습니다.


비지 박스 날짜

더 작은 장치 (설치할 아주 작은 실행 파일)에 사용되는 도구 : Busybox.

다음과 같이 busybox에 링크를 작성하십시오.

$ ln -s /bin/busybox date

그런 다음 이것을 호출하여 사용하십시오 date(PATH 포함 디렉토리에 배치).

또는 다음과 같은 별칭을 만드십시오.

$ alias date='busybox date'

Busybox 날짜에는 좋은 옵션이 있습니다. -D는 입력 시간 형식을 수신합니다. 그것은 시간으로 사용될 많은 형식을 엽니 다. -D 옵션을 사용하면 시간 10:33:56을 직접 변환 할 수 있습니다.

date -D "%H:%M:%S" -d "10:33:56" +"%Y.%m.%d-%H:%M:%S"

위 명령의 결과에서 볼 수 있듯이 그 날은 “오늘”인 것으로 가정합니다. 에포크에서 시작하는 시간을 얻으려면 :

$ string1="10:33:56"
$ date -u -D "%Y.%m.%d-%H:%M:%S" -d "1970.01.01-$string1" +"%Y.%m.%d-%H:%M:%S"
1970.01.01-10:33:56

Busybox 날짜는 -D 없이도 시간을 수신 할 수 있습니다 (위 형식).

$ date -u -d "1970.01.01-$string1" +"%Y.%m.%d-%H:%M:%S"
1970.01.01-10:33:56

그리고 출력 형식은 에포크 이후 몇 초가 될 수도 있습니다.

$ date -u -d "1970.01.01-$string1" +"%s"
52436

두 경우 모두 약간의 bash 수학 (busybox는 아직 수학을 수행 할 수 없음) :

string1="10:33:56"
string2="10:36:10"
t1=$(date -u -d "1970.01.01-$string1" +"%s")
t2=$(date -u -d "1970.01.01-$string2" +"%s")
echo $(( t2 - t1 ))

또는 형식 :

$ date -u -D "%s" -d "$(( t2 - t1 ))" +"%H:%M:%S"
00:02:14


답변

내가 한 방법은 다음과 같습니다.

START=$(date +%s);
sleep 1; # Your stuff
END=$(date +%s);
echo $((END-START)) | awk '{print int($1/60)":"int($1%60)}'

정말 간단합니다. 시작 시간 (초)을 마친 다음 끝 시간 (초)을 가져 와서 분 : 초 단위로 차이를 인쇄합니다.


답변

다른 옵션은 ( http://www.fresse.org/dateutils/#datediff ) datediff에서 사용하는 것 입니다 .dateutils

$ datediff 10:33:56 10:36:10
134s
$ datediff 10:33:56 10:36:10 -f%H:%M:%S
0:2:14
$ datediff 10:33:56 10:36:10 -f%0H:%0M:%0S
00:02:14

당신은 또한 사용할 수 있습니다 gawk. mawk1.3.4 또한이 strftimemktime의 있지만 이전 버전 mawk과는 nawk하지 않습니다.

$ TZ=UTC0 awk 'BEGIN{print strftime("%T",mktime("1970 1 1 10 36 10")-mktime("1970 1 1 10 33 56"))}'
00:02:14

또는 GNU로 다른 방법을 사용할 수 있습니다 date.

$ date -ud@$(($(date -ud'1970-01-01 10:36:10' +%s)-$(date -ud'1970-01-01 10:33:56' +%s))) +%T
00:02:14


답변

나는 date명령을 회상하지 않는 다른 방법을 제안하고 싶습니다 . 이미 %T날짜 형식으로 타임 스탬프를 수집 한 경우 도움이 될 수 있습니다 .

ts_get_sec()
{
  read -r h m s <<< $(echo $1 | tr ':' ' ' )
  echo $(((h*60*60)+(m*60)+s))
}

start_ts=10:33:56
stop_ts=10:36:10

START=$(ts_get_sec $start_ts)
STOP=$(ts_get_sec $stop_ts)
DIFF=$((STOP-START))

echo "$((DIFF/60))m $((DIFF%60))s"

같은 방법으로 밀리 초를 처리 할 수도 있습니다.

ts_get_msec()
{
  read -r h m s ms <<< $(echo $1 | tr '.:' ' ' )
  echo $(((h*60*60*1000)+(m*60*1000)+(s*1000)+ms))
}

start_ts=10:33:56.104
stop_ts=10:36:10.102

START=$(ts_get_msec $start_ts)
STOP=$(ts_get_msec $stop_ts)
DIFF=$((STOP-START))

min=$((DIFF/(60*1000)))
sec=$(((DIFF%(60*1000))/1000))
ms=$(((DIFF%(60*1000))%1000))

echo "${min}:${sec}.$ms"


답변

여기 마술이 있습니다 :

time1=14:30
time2=$( date +%H:%M ) # 16:00
diff=$(  echo "$time2 - $time1"  | sed 's%:%+(1/60)*%g' | bc -l )
echo $diff hours
# outputs 1.5 hours

seda :를 1/60으로 변환 할 수식으로 바꿉니다 . 그런 다음에 의해 만들어진 시간 계산bc


답변

현재 날짜 (GNU coreutils) 7.4 -d를 사용하여 산술을 수행 할 수 있습니다.

$ date -d -30days
Sat Jun 28 13:36:35 UTC 2014

$ date -d tomorrow
Tue Jul 29 13:40:55 UTC 2014

사용할 수있는 단위는 일, 년, 월, 시간, 분 및 초입니다.

$ date -d tomorrow+2days-10minutes
Thu Jul 31 13:33:02 UTC 2014