bash 스크립트를 사용하여 파일에서 날짜를 추출하고 유닉스 타임스탬프로 변환하는 방법은 무엇입니까?

bash 스크립트를 사용하여 파일에서 날짜를 추출하고 유닉스 타임스탬프로 변환하는 방법은 무엇입니까?

다음 내용이 포함된 파일이 있습니다(항목이 3개 이상일 수도 있음).

A Version: x_02.28.03.03 000000 aaa 2019/05/21 03:33:04
B Version: x_02.28.03.03 000000 aaa 2019/05/21 03:33:04
C Version: 0.01.011 #3 PREEMPT Tue Apr 4 09:14:17 UTC 2023

unix timestamp이제 모든 항목에서 날짜와 시간을 추출 하고 싶습니다 . 즉, 나는 2019/05/21 03:33:04, , 2019/05/21 03:33:04에 관심이 있다 Tue Apr 4 09:14:17 UTC 2023. 항목은 나중에 비교할 수 있도록 동일한 형식이어야 합니다. 또한 위치는 고정되지 않습니다(그러나 행의 마지막 두 필드가 됩니다).

bash다음은 스크립트의 일부 입니다 .

#!/bin/bash

ver_file="/home/test/tmp.txt"

ver_c=$(grep -E "C Version:" $ver_file | cut -d" " -f3-)

echo "$ver_c"

누군가 파일에서 날짜를 추출하는 방법을 말해 줄 수 있습니까?

추신: 저는 WSL2를 사용하여 Ubuntu에서 개발 중이지만 대상에서는 busybox date.

답변1

이 설명은 실제로 질문이 아니며 다소 혼란스럽습니다. 하지만 연애는 늘 즐겁기 때문에 도움이 되었으면 좋겠습니다.

구조화되지 않은 날짜를 추출하는 방법은 소스에 따라 다릅니다. 문서의 날짜는 악명이 높습니다. 주어진 예에서 내가 볼 수 있는 유일한 일관성은 날짜 문자열이 행의 끝에 있고 모두 열 6에서 시작한다는 것입니다. 그것이 제가 가장 먼저 찾는 것입니다.

위치가 "고정"되지 않은 경우 모두 6열에서 시작하지 않는다고 가정하면 세 번째 행의 날짜도 마지막 두 열이 아닙니다. 혼란스러운 예입니다. 어쨌든 그것은 가능합니다. 다양한 유형의 날짜 문자열과 각각을 처리하는 방법을 평가하려면 더 많은 논리가 필요합니다. 다시 말하지만 이는 실제로 입력 데이터 품질(GIGO)에 따라 달라집니다.

이는 GNU bash 및 핵심 유틸리티를 통해 다양한 방식으로 수행될 수 있습니다. 강조할 주요 도구는 날짜의 유효성을 평가하고 정규화하는 GNU date 명령입니다. 이 예에서 "UTC 2023"은기술적으로GNU 날짜가 잘못되지 않도록 하는 유효한 날짜입니다(그리고 bash로 캡처해야 합니다). 하지만 이와 같은 문제는 매우 간단하고 높은 정확도로 해결될 수 있습니다.

이와 같이 모든 날짜 문자열이 열 6에서 시작하거나 유효한 날짜가 마지막 두 열에 있다고 가정합니다.

while read line; do
    echo $line

    DATE_SIX="$(echo $line | cut -f6- -d' ')"
    if date --utc --date "${DATE_SIXE}" &> /dev/null; then
        DATE_SIX_NORMAL="$(date --utc --date "${DATE_SIX}")"
        DATE_SIX_EPOCH="$(date --utc --date "${DATE_SIX}" +%s)"
    else
        DATE_SIX_NORMAL="BAD DATE"
        DATE_SIX_EPOCH=0
    fi
    echo "DATE_SIX='${DATE_SIX}', DATE_SIX_NORMAL='${DATE_SIX_NORMAL}', DATE_SIX_EPOCH=${DATE_SIX_EPOCH}"

    DATE_LAST_TWO="$(echo $line | awk '{print $(NF-1)" "$(NF)}')"
    if [[ "${DATE_LAST_TWO}" != *":"* ]] || [[ "${DATE_LAST_TWO}" != *"/"* ]]; then
        # GNU date evaluates "UTC 2023" as a valid date, but it's not what's wanted ...
        DATE_LAST_TWO_NORMAL="BAD DATE"
        DATE_LAST_TWO_EPOCH=0
    else
        if date --utc --date "${DATE_LAST_TWO}" &> /dev/null; then
            DATE_LAST_TWO_NORMAL="$(date --utc --date "${DATE_LAST_TWO}")"
            DATE_LAST_TWO_EPOCH="$(date --utc --date "${DATE_LAST_TWO}" +%s)"
        else
            DATE_LAST_TWO_NORMAL="BAD DATE"
            DATE_LAST_TWO_EPOCH=0
        fi
    fi
    echo "DATE_LAST_TWO='${DATE_LAST_TWO}', DATE_LAST_TWO_NORMAL='${DATE_LAST_TWO_NORMAL}', DATE_LAST_TWO_EPOCH=${DATE_LAST_TWO_EPOCH}"

    echo
done < in.tmp

그 출력은 아래와 같습니다. 물론, DATE_EPOCH는 비교를 위한 정수로 사용될 수 있습니다.

A Version: x_02.28.03.03 000000 aaa 2019/05/21 03:33:04
DATE_SIX='2019/05/21 03:33:04', DATE_SIX_NORMAL='Tue May 21 03:33:04 AM UTC 2019', DATE_SIX_EPOCH=1558409584
DATE_LAST_TWO='2019/05/21 03:33:04', DATE_LAST_TWO_NORMAL='Tue May 21 03:33:04 AM UTC 2019', DATE_LAST_TWO_EPOCH=1558409584

B Version: x_02.28.03.03 000000 aaa 2019/05/21 03:33:04
DATE_SIX='2019/05/21 03:33:04', DATE_SIX_NORMAL='Tue May 21 03:33:04 AM UTC 2019', DATE_SIX_EPOCH=1558409584
DATE_LAST_TWO='2019/05/21 03:33:04', DATE_LAST_TWO_NORMAL='Tue May 21 03:33:04 AM UTC 2019', DATE_LAST_TWO_EPOCH=1558409584

C Version: 0.01.011 #3 PREEMPT Tue Apr 4 09:14:17 UTC 2023
DATE_SIX='Tue Apr 4 09:14:17 UTC 2023', DATE_SIX_NORMAL='Tue Apr  4 09:14:17 AM UTC 2023', DATE_SIX_EPOCH=1680599657
DATE_LAST_TWO='UTC 2023', DATE_LAST_TWO_NORMAL='BAD DATE', DATE_LAST_TWO_EPOCH=0

...cut 외에도 awk, bash 문자열 작업 등과 같은 다른 방법이 있습니다.

GNU date 명령은 타임스탬프를 변환하고 정규화할 수 있습니다.

또한 시간대가 없는 원래 날짜는 UTC라고 가정합니다.

그러나 사용자 정의 시간대를 지정할 수도 있습니다(예: 날짜 앞에 TZ 사용).

예를 들어,

$ date --utc --date="2019/05/21 03:33:04"
Tue May 21 03:33:04 AM UTC 2019
$ date --utc --date="Tue Apr 4 09:14:17 UTC 2023"
Tue Apr  4 09:14:17 AM UTC 2023

또는 원시 날짜 문자열을 에포크 시간으로 변환합니다...

$ date --utc --date="2019/05/21 03:33:04" +%s
1558409584
 date --utc --date="Tue Apr 4 09:14:17 UTC 2023" +%s
1680599657

...또는 man date(1) 또는 FORMAT 컨트롤의 옵션 조합을 사용합니다.

$ date --utc --date="2019/05/21 03:33:04" --rfc-email
Tue, 21 May 2019 03:33:04 +0000
$ date --utc --date="2019/05/21 03:33:04" +%Y%m%d%H%M%S
20190521033304
 date --utc --date="Tue Apr 4 09:14:17 UTC 2023" +%s
1680599657
$ TZ=America/New_York date --date="Tue Apr 4 09:14:17 UTC 2023"
Tue Apr  4 05:14:17 AM EDT 2023

비교를 위해 나는 unix epoch 타임스탬프를 선호합니다.

답변2

첫 번째 값에 대한 시간대를 정의하지 않았으므로 전 세계 어디에 있든 "현지 시간"이라고 가정합니다.

저는 여기에서 GNU를 사용 grep했습니다 date:

grep -oE '..../../.. ..:..:..$|... [[:digit:]]+ ..:..:.. [[:alnum:]]+ ....$' datafile |
    while IFS= read date
    do
        esec=$(date --date "$date" +%s)
        printf "%s --> %d\n" "$date" "$esec"
    done

예제 데이터의 경우 내(UK - GMT/BST) 시간대에서 실행할 때의 출력은 다음과 같습니다. GMT/BST 또는 이에 상응하는 WET 시간대에 있지 않는 한 초 값은 달라집니다.

2019/05/21 03:33:04 --> 1558405984
2019/05/21 03:33:04 --> 1558405984
Apr 4 09:14:17 UTC 2023 --> 1680599657

답변3

이 날짜는 perl로 쉽게 구문 분석됩니다 Date::Parse.

Unix epoch 시간을 앞에 추가하려면(숫자 비교 가능):

perl -MDate::Parse -pe '
  $_ = str2time(m{( 20\d\d/\d\d.*|\S+ \S+ \d+ \S+ UTC 20\d\d$)}) . " $_"
  ' < your-file

이것은 만든다:

1558409584 A Version: x_02.28.03.03 000000 aaa 2019/05/21 03:33:04
1558409584 B Version: x_02.28.03.03 000000 aaa 2019/05/21 03:33:04
1680599657 C Version: 0.01.011 #3 PREEMPT Tue Apr 4 09:14:17 UTC 2023

또는 ISO8601 스타일 시간 형식의 경우(어휘 비교 가능):

perl -MDate::Parse -MPOSIX -pe '
  $_ = strftime("%FT%T", strptime m{(20\d\d/\d\d.*|\S+ \S+ \d+ \S+ UTC 20\d\d$)}) . " $_"
  ' < your-file

이것은 만든다:

2019-05-21T02:33:04 A Version: x_02.28.03.03 000000 aaa 2019/05/21 03:33:04
2019-05-21T02:33:04 B Version: x_02.28.03.03 000000 aaa 2019/05/21 03:33:04
2023-04-04T09:14:17 C Version: 0.01.011 #3 PREEMPT Tue Apr 4 09:14:17 UTC 2023

관련 정보