더 큰 스크립트의 일부로 awk
임의의 날짜 문자열을 에포크 이후의 초로 변환해야 합니다. 이것은 awk
함수로 작동 하지 않기 때문에 date
각 입력 라인에 대해 호출할 수 있다고 생각했습니다. (돌이켜보면 그걸 사용할 수도 있었지만 perl
, 그 생각은 버리자.)
예상치 못한 결과를 본 후 문제를 이 문제( bash
및 GNU awk
) 로 단순화했습니다.
for f in {1..5}; do echo $f; sleep 2; done | awk '{ "date" | getline x; printf ">>%s<<\n", x }'
awk
루프가 실제로 2초에 한 번만 실행된다는 것을 확인했지만 결과는 모두 동일합니다.
>>29 Jun 2020 10:38:24<<
>>29 Jun 2020 10:38:24<<
>>29 Jun 2020 10:38:24<<
>>29 Jun 2020 10:38:24<<
>>29 Jun 2020 10:38:24<<
캐싱 일 수도 있습니다 getline
. 그래서 나는 이것을 시도했다
for f in {1..5}; do echo $f; sleep 2; done | awk '{ "date; : " NR | getline x; printf ">>NR=%d - %s<<\n", NR, x }'
>>NR=1 - 29 Jun 2020 10:44:05<<
>>NR=2 - 29 Jun 2020 10:44:07<<
>>NR=3 - 29 Jun 2020 10:44:09<<
>>NR=4 - 29 Jun 2020 10:44:11<<
>>NR=5 - 29 Jun 2020 10:44:13<<
모든 것이 좋아 보입니다. 캐싱(이 경우)이 비활성화되고 date
.
그런 다음 다시 이 경로를 따라 계속해서 파이프된 명령에 반복된 값을 제공했습니다.getline
for f in 1 2 1 1 2 3; do echo $f; sleep 2; done | awk '{ "date; : " $1 | getline x; printf ">>NR=%d - f=%d - %s<<\n", NR, $1, x }'
>>NR=1 - f=1 - 29 Jun 2020 10:43:01<<
>>NR=2 - f=2 - 29 Jun 2020 10:43:03<<
>>NR=3 - f=1 - 29 Jun 2020 10:43:03<<
>>NR=4 - f=1 - 29 Jun 2020 10:43:03<<
>>NR=5 - f=2 - 29 Jun 2020 10:43:03<<
>>NR=6 - f=3 - 29 Jun 2020 10:43:11<<
나는 3번째 줄에서 명령이 새로 평가되거나(새 날짜 값 제공) 첫 번째 줄의 값이 반복될 것으로 예상했습니다. 둘 다 일어나지 않았습니다.
이것은 나를 당황하게 만들었습니다. 왜 행 2-5에서 동일한 값을 얻는지 이해할 수 없습니다. 에서 로 f
변경하면 진행 중인 캐싱이 눈에 띄게 비활성화됩니다. 그러나 뒤에서 로 변경하면 첫 번째 항목의 캐시된 복사본이 제공되지 않고 값의 복사본이 제공됩니다 . 명령 문자열을 새 값으로 변경하고 에 대한 새 호출을 트리거합니다 .1
2
f
2
1
f=1
f=2
f=3
date
왜?
답변1
GNU awk 매뉴얼에는 다음이 언급되어 있습니다.저것:
getline
awk 프로그램 실행 중에 동일한 파일 이름이나 동일한 쉘 명령이 여러 번 사용되는 경우(참조명시적 입력getline
), 파일을 처음 열 때(또는 명령을 실행할 때)에만 해당됩니다. 이 시점에서 파일이나 명령에서 첫 번째 입력 레코드를 읽습니다. 다음에 동일한 파일이나 명령이 사용될 때 해당 파일이나 명령getline
에서 다른 레코드를 읽는 식으로 진행됩니다.
따라서 명령을 한 번만 실행하고 추가 읽기에서 EOF를 얻으므로 이전 값은 x
변경되지 않습니다. x
읽을 때마다 버리면 어떻게 되는지 비교해 보세요 .
$ for f in {1..3}; do echo $f; sleep 2; done |
awk '{ "date" | getline x; printf ">>%s<<\n", x; x ="done" }'
>>Mon Jun 29 13:37:53 EEST 2020<<
>>done<<
>>done<<
여기의 명령을 명령이 실행된 시점의 기록을 저장하는 명령으로 바꾸면 date
해당 명령이 한 번만 실행되었음을 나타내는 기록도 볼 수 있습니다.
getline
EOF에서는 0을 반환하고 오류에서는 -1을 반환하므로 다음을 확인할 수 있습니다.
$ for f in {1..3}; do echo $f; sleep 2; done |
awk '{ if (("date" | getline x) > 0) printf ">>%s<<\n", x;
else printf "error or eof\n"; }'
>>Mon Jun 29 13:46:58 EEST 2020<<
error or eof
error or eof
close()
다음에 다시 열도록 awk에 지시하려면 명시적으로 파이프를 사용해야 합니다 .
$ for f in {1..3}; do echo $f; sleep 2; done |
awk '{ "date" | getline x; printf ">>%s<<\n", x; x = "done"; close("date") }'
>>Mon Jun 29 13:39:19 EEST 2020<<
>>Mon Jun 29 13:39:21 EEST 2020<<
>>Mon Jun 29 13:39:23 EEST 2020<<
를 사용하면 "date; : " NR | getline x;
모든 명령줄이 다르므로 각각에 대해 별도의 파이프라인이 있습니다.
"date; : " $1 | getline x;
을 사용하면 $1
반복할 때 첫 번째 경우와 동일한 문제가 발생하며 동일한 파이프에 대한 두 번째 읽기에서 EOF가 발생합니다.
답변2
"임의의 형식"이 무엇을 의미하는지 잘 모르겠지만 GNU/awk 시간 루틴은 date 명령이 수행할 수 있는 모든 작업과 그 이상을 수행할 수 있습니다. 실제 입력 내용을 보여주고 싶다면 이 데모를 실제 애플리케이션에 적용하는 방법을 설명해 드릴 수 있습니다.
이 스크립트는 텍스트 날짜를 임의의 순서(월 이름 포함)에서 datespec 형식으로 변환한 다음 에포크 이후의 초 단위(외부 날짜 명령을 사용하여 확인)로 변환한 다음 ISO 형식으로 변환한 다음 조정을 통해 ISO 형식으로 변환하는 방법을 보여줍니다. : 월, 일, 분.
스크립트:
#! /bin/bash
AWK='
BEGIN {
#.. Set up conversion from month names to numeric.
split ("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec", V, / /);
for (k in V) iMth[V[k]] = k;
#.. These are only needed to verify the epoch using /bin/date.
fmtSys = "echo -n \047/bin/date: \047; date -d \047 %s + %d secs\047 \047%s\047\n";
Base = "1970-01-01 00:00:00 UTC";
Date = "+%Y-%m-%d %T";
}
function Show (V, Local, ds, epoch) {
printf ("\n");
ds = sprintf ("%d %d %d %d %d %d %d", V[8], V[10], V[2], V[4], V[5], V[6], -1);
epoch = mktime (ds);
printf ("datespec: %s; epoch: %d\n", ds, epoch);
printf ("ISO: %s\n", strftime ("%F %T %Z Week %W Day %w", epoch));
#.. Call date command to verify.
system (sprintf (fmtSys, Base, epoch, Date));
}
function Fix (tx, Local, ds, V) {
split (tx, V, /[^A-Za-z0-9]/);
V[10] = iMth[V[3]];
Show( V);
printf ("\n.. Go back 10 months and 43 days\n");
V[10] -= 10; V[2] -= 43;
Show( V);
printf ("\n.. and forward 427 minutes\n");
V[5] += 427;
Show( V);
}
{ printf ("\n.. Input date ::%s::\n", $0); }
{ Fix( $0); }
'
printf 'Mon 29 Jun 16:04:42 BST 2020\n' | awk "${AWK}"
시험을 치르다:
paul $ ./myDate
.. Input date ::Mon 29 Jun 16:04:42 BST 2020::
datespec: 2020 6 29 16 4 42 -1; epoch: 1593443082
ISO: 2020-06-29 16:04:42 BST Week 26 Day 1
/bin/date: 2020-06-29 16:04:42
.. Go back 10 months and 43 days
datespec: 2020 -4 -14 16 4 42 -1; epoch: 1563375882
ISO: 2019-07-17 16:04:42 BST Week 28 Day 3
/bin/date: 2019-07-17 16:04:42
.. and forward 427 minutes
datespec: 2020 -4 -14 16 431 42 -1; epoch: 1563401502
ISO: 2019-07-17 23:11:42 BST Week 28 Day 3
/bin/date: 2019-07-17 23:11:42
paul $
답변3
ISO8601 형식의 날짜 필드가 포함된 로그 항목이 포함된 파일을 처리하는 동안 비슷한 문제에 직면했습니다. 어떤 단계에 시간이 걸리는지 알아보기 위해 연속된 줄 간의 차이점을 찾으려고 했습니다.
처음에 시도했지만 몇 군데에서 문제가 있었던 코드는 다음과 같습니다.
BEGIN {
FS="|"
}
{
#Not Working Script - Reason close() command was not done in date function
"date -d " "\"" $1 "\"" " \"" "+%s%3N" "\""| getline curr_rec_ts;
close(curr_rec_ts)
elapsed=curr_rec_ts-prev_rec_ts
print prev_rec_ts"|"curr_rec_ts"|"elapsed
prev_rec_ts=curr_rec_ts
prev_rec=$1"|"$7"|"$8"|"$12
}END {
}
입력 파일에는 다음 레코드가 포함되어 있습니다.
# Input File
2024-03-08T18:34:09,669
2024-03-08T18:34:09,679
2024-03-08T18:34:09,679
2024-03-08T18:34:09,621
2024-03-08T18:34:09,621
2024-03-08T18:34:09,621
2024-03-08T18:34:09,667
2024-03-08T18:34:09,667
2024-03-08T18:34:09,668
2024-03-08T18:34:09,668
2024-03-08T18:34:09,669 // Many more
2024-03-08T18:34:09,669
2024-03-08T18:34:09,669
2024-03-08T18:34:09,669
2024-03-08T18:34:09,669 //Issue occured at this comparison
2024-03-08T18:34:09,861
2024-03-08T18:34:09,861
주어진 차이는 193밀리초입니다. 이상적으로는 192밀리초가 되어야 합니다.
문제는 getline이 반복되는 날짜 호출을 캐싱하고 후속 작업에 유사한 응답을 제공하기 때문에 발생합니다.
해결책은
#Apply Command and store in a variable
cmd="date -d " "\"" $1 "\"" " \"" "+%s%3N" "\""
#
cmd| getline curr_rec_ts;
# Key was this Below
close(cmd)
요약 다음은 종료에 적용되지 않습니다
사례 1
Getline:: "date -d " "\"" $1 "\"" " \"" "+%s%3N" "\""| getline curr_rec_ts;
Close :: close("date") Didnt work as Original Command is different
사례 2
Getline :: "date -d " "\"" $1 "\"" " \"" "+%s%3N" "\""| getline curr_rec_ts;
Close :: close(curr_rec_ts) // Didnt work as it as no mean to close variable
사례 3
GetLine :: cmd="date -d " "\"" $1 "\"" " \"" "+%s%3N" "\""
Close :: close(cmd) // work as full command was passed.