awk를 통해 날짜로부터 잘못된 응답 받기

awk를 통해 날짜로부터 잘못된 응답 받기

인터페이스의 마지막 입력 및 출력을 표시하는 Cisco 스위치의 출력을 유용한 형식으로 변환할 수 있는 스크립트를 만들려고 합니다. 이는 예를 들어 지난 6개월 동안 어떤 인터페이스가 사용되지 않았는지 알 수 있도록 하기 위함입니다. Cisco에서는 인터페이스에 대한 마지막 입력이 10w4d.

지금까지 얻은 것은 다음과 같은 입력 파일입니다.

FastEthernet0/4,3 days ago,3 days ago
FastEthernet0/8,46 years ago ,46 years ago 
FastEthernet0/10,46 years ago ,46 years ago 
FastEthernet0/11,46 years ago ,46 years ago 
FastEthernet0/12,08:47:53,04:00:32
FastEthernet0/13,1 year ago 46 weeks ago ,1 year ago 46 weeks ago 
FastEthernet0/14,46 years ago ,46 years ago 
FastEthernet0/15,13 weeks ago 4 days ago ,13 weeks ago 4 days ago 
FastEthernet0/16,46 years ago ,46 years ago 
FastEthernet0/18,46 years ago ,46 years ago 
FastEthernet0/19,46 years ago ,46 years ago 
FastEthernet0/20,46 years ago ,46 years ago 
FastEthernet0/22,46 years ago ,1 year ago 50 weeks ago 
FastEthernet0/24,46 years ago ,46 years ago

유효한 날짜를 얻을 수 있도록 Never Last Input and Output 상태의 모든 인터페이스를 46년 전으로 설정했습니다. 예, 이를 고려할 수 있는 가동 시간 기록이 있습니다.

내 문제는 이제 유효한 날짜로 변환하려고 한다는 것입니다. 이렇게 하면 awk정상적으로 작동합니다.xargs

awk -F, '{print $2}' tst | xargs -i date -d "{}" +%Y-%m-%d

나는 다음과 같은 응답을 받았습니다.

2016-08-18
1970-08-21
1970-08-21
1970-08-21
2016-08-21
2014-10-03
1970-08-21
2016-05-18
1970-08-21
1970-08-21
1970-08-21
1970-08-21
1970-08-21
1970-08-21

하지만 이렇게 하면 다음과 같은 결과가 나올 것 같습니다.

awk -F, -v OFS="," '{
    cmd2=("date \"+'%Y-%m-%d'\" -d \""$2 "\"")
    cmd2|getline d2
    print cmd2,d2
}' tst

나는 다음과 같은 응답을 받았습니다.

date "+%Y-%m-%d" -d "3 days ago",2016-08-18
date "+%Y-%m-%d" -d "46 years ago ",1970-08-21
date "+%Y-%m-%d" -d "46 years ago ",1970-08-21
date "+%Y-%m-%d" -d "46 years ago ",1970-08-21
date "+%Y-%m-%d" -d "08:47:53",2016-08-21
date "+%Y-%m-%d" -d "1 year ago 46 weeks ago ",2014-10-03
date "+%Y-%m-%d" -d "46 years ago ",2014-10-03
date "+%Y-%m-%d" -d "13 weeks ago 4 days ago ",2016-05-18
date "+%Y-%m-%d" -d "46 years ago ",2016-05-18
date "+%Y-%m-%d" -d "46 years ago ",2016-05-18
date "+%Y-%m-%d" -d "46 years ago ",2016-05-18
date "+%Y-%m-%d" -d "46 years ago ",2016-05-18
date "+%Y-%m-%d" -d "46 years ago ",2016-05-18
date "+%Y-%m-%d" -d "46 years ago ",2016-05-18

cmd2방금 명령에 추가하여 어떤 작업을 수행하는지 확인했습니다. print이 명령을 실행하면 정확한 날짜가 표시됩니다.

간단한 내용이 빠진 것 같은데 3~4시간 동안 반복해서 시도한 결과 이제 다시 한 번 살펴보고 싶습니다.

누군가 나에게 올바른 방향을 알려줄 수 있습니까?

고쳐 쓰다! Stephen도 지적했듯이 입력 파일 세 줄만 있어도 아무런 문제가 없었습니다. 테스트되지 않은 시나리오를 게시하여 죄송합니다.

답변1

이 작업을 수행할 때:

cmd = "some command"
cmd | getline d2
cmd | getline d2

에서 awk첫 번째는 getline출력의 첫 번째 줄(레코드)을 가져오고 두 번째는 두 번째 줄을 가져옵니다. 출력의 끝에 도달하면 0이 반환됩니다.cmdcmdgetlinegetline

귀하의 경우 cmd = "date -d \"46 years ago\""명령은 한 줄만 출력하므로 첫 번째 명령은 해당 줄 과 양수를 getline반환 하지만 두 번째 명령은 도착하므로 0을 반환하고 변경되지 않습니다.d2eofd2

여기에는 다음이 필요합니다.

 cmd | getline d2; close(cmd)

그래서 cmd매번 실행해야 합니다.

또는 동일한 매개변수로 동일한 명령을 여러 번 실행하지 않도록 출력을 cmd해시에 저장합니다.

if (!($2 in cache)) {cmd=...; cmd | getline cache[$2]; close(cmd)}
print cache[$2]

cmd많은 파일 설명자를 열지 않으려면 해당 위치를 닫는 것이 좋습니다.


close(cmd)when 의 모호성은 및 cmd에 모두 적용됩니다 . 폐쇄 여부getline < cmdcmd | getlinecmd 주문하다또는cmd 문서아니면 둘다?

$ cat test.awk
BEGIN{
  OFS=","
  getline a1 < "uname"
  "uname" | getline b1
  close("uname")

  getline a2 < "uname"
  "uname" | getline b2
  print a1,b1,a2,b2
}
$ echo test > uname
$ mawk -f test.awk
test,Linux,test,Linux
$ bwk-awk -f test.awk
test,Linux,test,Linux
$ gawk -f test.awk
test,Linux,,Linux
$ busybox awk -f test.awk
test,,test,

(Brian Kernighan(at)이 관리하며 bwk-awkSolaris나 FreeBSD 등을 기반으로 한 awk 구현과 유사하게 작동합니다.)kawknawkawk

"uname"busybox awk를 사용하여 파일과 명령을 동시에 사용할 수 없는 방법 과 닫히지 않는 getline방법에 대해 알아보세요.close("uname")uname 문서존재하다 gawk.

따라서 동일한 이름을 가진 파일과 명령을 동시에 사용하지 않는 것이 가장 좋습니다. "\n"시작이나 끝 부분에 추가 할 수 있습니다 .주문하다와 연관될 가능성을 낮추는 것문서.

좋다:

 awk 'BEGIN {getline foo < $ENVIRON["FILE"]; "uname\n" | getline system}'

$FILE만약 그렇다면, 이 문제는 피할 수 있을 것입니다 uname( $FILE그럴 경우는 아니지만 uname<newline>가능성은 낮습니다).

답변2

흥미로운 점은 이 구조에서 awk이전에 이 호출을 수행했다는 사실을 기억하고 다시 수행하지 않기 때문에 데이터가 readline.

우리는 이것을 함으로써 이것을 볼 수 있습니다strace -f -o xxx awk ....

추적을 통해 다음을 확인할 수 있습니다.

3401  execve("/bin/date", ["date", "+%Y-%m-%d", "-d", "3 days ago"], [/* 50 vars */]) = 0
3403  execve("/bin/date", ["date", "+%Y-%m-%d", "-d", "46 years ago "], [/* 50 vars */]) = 0
3405  execve("/bin/date", ["date", "+%Y-%m-%d", "-d", "08:47:53"], [/* 50 vars */]) = 0
3407  execve("/bin/date", ["date", "+%Y-%m-%d", "-d", "1 year ago 46 weeks ago "], [/* 50 vars */]) = 0
3409  execve("/bin/date", ["date", "+%Y-%m-%d", "-d", "13 weeks ago 4 days ago "], [/* 50 vars */]) = 0

따라서 각 고유 날짜는 한 번만 통과되지만 다시는 통과되지 않습니다. 이는 CentOS 7 및 Debian의 GNU awk에서 발생하므로 mawk이는 예상된 동작인 것 같습니다.

간단한 방법은 각 줄을 고유하게 만드는 것입니다. 예를 들어 줄 번호와 함께 주석을 추가합니다.

cmd2="date \"+'%Y-%m-%d'\" -d \""$2 "\" ; # " NR 

생성된 코드는 다음과 같습니다.

awk -F, -v OFS="," '{cmd2="date \"+'%Y-%m-%d'\" -d \""$2 "\" ; # " NR ;
                     cmd2|getline d2;
                     print cmd2,d2}' x
date "+%Y-%m-%d" -d "3 days ago" ; # 1,2016-08-18
date "+%Y-%m-%d" -d "46 years ago " ; # 2,1970-08-21
date "+%Y-%m-%d" -d "46 years ago " ; # 3,1970-08-21
date "+%Y-%m-%d" -d "46 years ago " ; # 4,1970-08-21
date "+%Y-%m-%d" -d "08:47:53" ; # 5,2016-08-21
date "+%Y-%m-%d" -d "1 year ago 46 weeks ago " ; # 6,2014-10-03
date "+%Y-%m-%d" -d "46 years ago " ; # 7,1970-08-21
date "+%Y-%m-%d" -d "13 weeks ago 4 days ago " ; # 8,2016-05-18
date "+%Y-%m-%d" -d "46 years ago " ; # 9,1970-08-21
date "+%Y-%m-%d" -d "46 years ago " ; # 10,1970-08-21
date "+%Y-%m-%d" -d "46 years ago " ; # 11,1970-08-21
date "+%Y-%m-%d" -d "46 years ago " ; # 12,1970-08-21
date "+%Y-%m-%d" -d "46 years ago " ; # 13,1970-08-21
date "+%Y-%m-%d" -d "46 years ago " ; # 14,1970-08-21

더 복잡한 해결책은 결과를 배열에 캐시하고 date이전에 이 날짜를 본 적이 없는 경우에만 호출하는 것입니다.

답변3

2열과 3열의 상대 날짜를 실제 날짜로 바꿉니다.

awk -F, '{for(i=2;i<4;i++){"date -I -d \""$i"\"" | getline a; $i=a}}1' OFS=, tst

직접적으로 작동하지 않는다는 사실에 놀랐습니다 getline $i. 두 필드 중 하나를 변경하거나 아무것도 변경하지 않습니다. 따라서 a
정의된 사용자 함수에 추가 변수를 포함 해야 합니다 .

awk -F, '
    function cmd2(date){
        "date -I -d \""date"\"" | getline a
        return a
    }
    {
        $2=cmd2($2)
        $3=cmd2($3)
        print
    }' OFS=, tst

그러나 함수가 지역 변수로 정의되면 cmd2(date, a)위와 동일한 동작을 나타냅니다.

동질적인 라인 작업을 쉽게 완료할 수 있습니다.GNU sed

tr ',' '\n' tst |
sed '1~3!{s/^/date -I -d "/;s/$/"/e;}' |
paste -sd ',,\n'

관련 정보