AWK: 따옴표로 묶인 여러 단어가 포함된 열을 올바르게 표시하는 방법은 무엇입니까?

AWK: 따옴표로 묶인 여러 단어가 포함된 열을 올바르게 표시하는 방법은 무엇입니까?

나는 다음과 같이 awk를 사용합니다.

grep -i 'logged in' path-to-file | tail -n -10 | awk '{ print $6, "logged in on ",substr($2,1,8),$1"."; }' | sed 's/"//g'

그러나 $6열 에서는 "nickname"사용자가 생성하므로 때로는 단어 열일 때도 있고 여러 단어로 구성된 경우도 있습니다.

2017-12-21 21:54:01.714540 사용자 #41 닉네임: "sarah the Princes" 사용자 이름: "guest" IP 주소: 111111111, UDP 주소: udp 로그인되었습니다.

사라 왕자의 별명 전체를 인쇄하는 대신 사라라는 첫 번째 단어만 표시합니다.

답변1

awk의 gsub()기능을 사용하여 "and "(따옴표 뒤에 공백이 있고 공백 뒤에 따옴표가 오는)를 임의의 구분 기호로 바꾸고 FS를 해당 구분 기호로 설정하고 원하는 것을 추출할 수 있습니다. FS를 변경하면 필드 수도 변경됩니다. 또한 다음 입력 행을 올바르게 처리하려면 FS를 원래 값으로 다시 재설정해야 합니다.

귀하의 경우에는 필드에서 일부 데이터(날짜 및 시간)를 추출하고 싶습니다.앞으로FS가 달라졌습니다.

예를 들어 ./file행이 5개 있는 경우 각 행은 제공한 예제 행의 정확한 복사본입니다.

$ grep -i 'logged in' ./file | tail | awk '
{ d=$1;
  t=$2; sub(/\..*/,"",t);

  FS="XXX";
  gsub(/" | "/,"XXX",$0);
  print $2,"logged in at", t, d;
  FS="[[:space:]]+"
}'
sarah the princes logged in at 21:54:01 2017-12-21
sarah the princes logged in at 21:54:01 2017-12-21
sarah the princes logged in at 21:54:01 2017-12-21
sarah the princes logged in at 21:54:01 2017-12-21
sarah the princes logged in at 21:54:01 2017-12-21

나는 사용했다트리플 엑스입력의 어느 곳에도 표시되지 않기 때문에 필드 구분 기호로 사용됩니다. 이 예에서 탭은 잘 작동하지만 필드 구분 기호가 단일 문자일 필요는 없다는 것을 증명하지는 않습니다. 탭이 있는 단일 문자를 식별할 수 없거나 쉽게 식별할 수 없는 경우 중요합니다. 입력의 어느 곳에서도 사용되지 않습니다.

필드 데이터를 추출해야 하면 상황이 더 복잡해집니다.뒤쪽에큰따옴표로 묶인 필드(예: IP 주소 또는 UDP 포트 필드) - gsub해당 필드 번호가 무엇인지 알 수 없기 때문에 이전에는 추출할 수 없습니다. 나는 perl이 시점에서 @Wildcard를 사용하고 싶지만 (또는 심지어 sed@Wildcard의 답변처럼) 함수 호출의 정규식을 적절하게 awk확장하는 것이 한 가지 접근 방식입니다 . gsub예를 들어 스크립트를 다음으로 바꿉니다 awk.

$ grep -i 'logged in' ./file | tail | awk '
{   d=$1;
    t=$2;
    sub(/\..*/,"",t);

    FS="XXX";
    gsub(/" | "|address: |, /,"XXX",$0);
    sub(/ .*/,"",$8);      # get rid of trailing junk after udp port

    print $2,"logged in at", t, d, "as" ,$4, "from", $6":"$8;

    FS="[[:space:]]+"
}'

다음과 같은 출력이 생성됩니다.

sarah the princes logged in at 21:54:01 2017-12-21 as guest from 111111111:udp
sarah the princes logged in at 21:54:01 2017-12-21 as guest from 111111111:udp
sarah the princes logged in at 21:54:01 2017-12-21 as guest from 111111111:udp
sarah the princes logged in at 21:54:01 2017-12-21 as guest from 111111111:udp
sarah the princes logged in at 21:54:01 2017-12-21 as guest from 111111111:udp

perl완벽을 기하기 위해 Perl 코어 모듈을 사용하는 한 가지 방법은 다음과 같습니다 Text::ParseWords.

#!/usr/bin/perl

use strict;
use Text::ParseWords;

my $keep=1;  # keep " chars in output.  set to 0 to strip them.

while(<>) {
  my @F = quotewords('\s+', $keep, $_);

  $F[1] =~ s/\..*//;  # strip decimal fraction from time field
  $F[10] =~ s/,//;    # strip trailing comma from IP address field

  # remember: perl array indices start at zero, not one.
  printf "%s logged in at %s %s as %s from %s:%s\n", @F[5,1,0,7,10,13];
}

quotewords()from 함수를 사용하여 Text::Parsewords각 입력 행을 필드(라는 배열에 저장됨)로 분할하고 일부 필드를 약간 정리한 다음 다음을 사용하여 @F필수 필드를 인쇄합니다.printf

한 줄로 말하면 다음과 같습니다.

grep -i 'logged in' ./file | tail | perl -MText::ParseWords -n -e '
  @F = quotewords(q/\s+/, 1, $_);
  $F[1] =~ s/\..*//;
  $F[10] =~ s/,//;
  printf "%s logged in at %s %s as %s from %s:%s\n", @F[5,1,0,7,10,13]'

내가 어떻게 변경했는지 주목하세요 '/s+'- q/\s+/Perl에는 훌륭한 기능이 있습니다참조 연산자이를 방지하는 데 사용할 수 있습니다.작은따옴표 안의 작은따옴표 관련 문제.

답변2

사이즈에 맞게 입어보세요:

sed -En '
  /^(....-..-..) (..:..:..)[^:]*nickname: "?([^":]+)"? username:.*logged in.*$/ {
    s//\3 logged in at \2 on \1./p
  }
' path-to-file | tail -n 10

관련 정보