파이프 '"cmd"의 awk getline | '"cmd" getline var' 파이프의 awk getline은 해당 값을 캐시합니다.

파이프 '"cmd"의 awk getline | '"cmd" getline var' 파이프의 awk getline은 해당 값을 캐시합니다.

더 큰 스크립트의 일부로 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변경하면 진행 중인 캐싱이 눈에 띄게 비활성화됩니다. 그러나 뒤에서 로 변경하면 첫 번째 항목의 캐시된 복사본이 제공되지 않고 값의 복사본이 제공됩니다 . 명령 문자열을 새 값으로 변경하고 에 대한 새 호출을 트리거합니다 .12f21f=1f=2f=3date

왜?

답변1

GNU awk 매뉴얼에는 다음이 언급되어 있습니다.저것:

getlineawk 프로그램 실행 중에 동일한 파일 이름이나 동일한 쉘 명령이 여러 번 사용되는 경우(참조명시적 입력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해당 명령이 한 번만 실행되었음을 나타내는 기록도 볼 수 있습니다.

getlineEOF에서는 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.

관련 정보