awk를 사용하여 두 문자열 중 하나를 포함하는 테이블의 모든 필드를 인쇄하는 방법

awk를 사용하여 두 문자열 중 하나를 포함하는 테이블의 모든 필드를 인쇄하는 방법

행이 많고 행당 열 수가 가변적인 테이블이 있습니다.

각 줄에서 첫 번째 필드와 두 문자열 중 하나가 포함된 모든 필드만 인쇄하려고 합니다(이 경우 dog 및 cow라는 단어가 포함된 모든 필드가 필요합니다).

예를 들어:

A   dog999   dog284   cow284   pig383   pig234   cow432   chicken432
B   cow394   cow432   cow345   dog983   pig345   chicken532 
C   dog847   pig357   pig236   cow395   dog496
D   dog392   cow237   cow749

원하는 출력:

A   dog999   dog284   cow284   cow432   
B   cow394   cow432   cow345   dog983   
C   dog847   cow395   dog496
D   dog392   cow237   cow749

지금까지 나는 awk를 사용했습니다.

awk -v OFS='\t' '{for (i = 1; i <= NF; i++) {if ($i ~ /dog/) print $1,$i; else if ($i ~ /cow/) print $1,$i} }' file.txt

그러나 이렇게 하면 각 필드에 두 문자열 중 하나가 한 줄을 차지하게 됩니다.

답변1

perl해결 방법이 괜찮은 경우 :

$ cat ip.txt 
A   dog999   dog284   cow284   pig383   pig234   cow432   chicken432
B   cow394   cow432   cow345   dog983   pig345   chicken532 
C   dog847   pig357   pig236   cow395   dog496
D   dog392   cow237   cow749

$ perl -lane 'print join("\t",$F[0],grep {/cow|dog/} @F[1..$#F])' ip.txt 
A   dog999  dog284  cow284  cow432
B   cow394  cow432  cow345  dog983
C   dog847  cow395  dog496
D   dog392  cow237  cow749
  • -a입력 줄을 공백으로 분할하고 @F배열 에 저장
  • -l입력에서 개행 문자를 제거하고 인쇄할 때 다시 추가하세요.
  • join\t인쇄할 때,
  • $F[0],grep {/cow|dog/} @F[1..$#F]배열의 첫 번째 요소와 cow일치하는 모든 요소 또는dog
  • 또한 사용 가능합니다 perl -lape'$_=join"\t",shift(@F),grep/cow|dog/,@F'. 여기에서는 배열의 첫 번째 요소를 shift제거하고 반환하며 , 결과를 에 할당하면 끝에 무료 옵션이 인쇄됩니다 (힌트).@F$_-p스티븐 차제라스)


다음을 포함하지 않거나 cow무시 dog되는 줄:

perl -lane 'print join("\t",$F[0],grep {//} @F[1..$#F]) if /cow|dog/' ip.txt 

답변2

거의 비슷하지만 일치하는 모든 단어에 대해 인쇄하고 싶지 않기 때문에 첫 번째 값을 추출해야 합니다. 이를 사용하여 printf줄바꿈을 피할 수 있습니다.

awk '{printf "%s",$1
      for (i=1;i<=NF;i++)
      {
        if ($i ~ /dog|cow/) { printf " %s",$i; }
      }
      print ""
     }'

출력은 다음과 같습니다:

A dog999 dog284 cow284 cow432
B cow394 cow432 cow345 dog983
C dog847 cow395 dog496
D dog392 cow237 cow749

이는 한 줄로 축소할 수 있습니다.

awk '{printf "%s",$1; for (i=1;i<=NF;i++) { if ($i ~ /dog|cow/) { printf " %s",$i; }  } print ""  }'

이렇게 하면 어떤 단어와도 일치하지 않는 줄이 인쇄됩니다.

E pig sheep

출력됩니다

E

답변3

TxR 앗 매크로:

$ txr -e '(awk (:let tmp)
               (:begin (set ofs "\t"))                     
               (f (set tmp (pop f))
                  (ff (keep-if #/cow|dog/))
                  (push tmp f) (prn)))' data
A   dog999  dog284  cow284  cow432
B   cow394  cow432  cow345  dog983
C   dog847  cow395  dog496
D   dog392  cow237  cow749

분해:

  1. :let매크로의 절은 지역 변수를 지정합니다. 이 매크로는 "Awk Paradigm"을 구현하지만 사용하기 전에 변수를 정의해야 하는 유형 안전 언어로 구현됩니다. 따라서 및 ( POSIX Awk 와 유사)과 :begin같은 절 외에도 이 Awk는 어휘 범위 매크로로 정의된 변수도 제공합니다.:endBEGINEND:let

  2. (f (set tmp (pop f)) ...)조건이 인 조건부 작업 절입니다 f. 레코드의 구분된 필드 목록인 경우 비어 있지 않으면( 와 같지 않음 nil) 부울 true처럼 작동합니다. 따라서 에 아무것도 없으면 작업 양식이 실행됩니다 f.

  3. (set tmp (pop f))목록에서 첫 번째 필드를 팝하고 임시 변수에 저장합니다 tmp. 두 번째 필드가 첫 번째 필드가 되고, 세 번째 필드가 두 번째 필드가 되는 식입니다. f레코드는 필드를 사용하여 레코드가 재구성되는 POSIX Awk와 마찬가지로 작업할 때 rec자동으로 재구성됩니다 .ofs$0OFS

  4. (ff ...)이 경우 작업별로 필드를 필터링합니다 (keep-if #/regex/). 기본적으로 f정규식과 일치하지 않는 모든 필드를 제거합니다. ff매크로 내부에 표시되는 연산자입니다 awk. keep-if일반 함수입니다. 여기서는 암시적으로 카레되므로 목록 인수가 표시되지 않습니다. 조건자 함수가 필요하지만 정규 표현식은 함수 호출 가능하므로 조건자로 적합합니다.

  5. 그런 다음 이전에 저장한 첫 번째 필드를 다시 필드 f목록 에 푸시합니다 (push tmp f).

  6. (prn)와 동일합니다 print. 인수가 없으면 레코드와 ors개행 문자로 초기화되는 출력 레코드 구분 기호( )를 인쇄합니다. rec의 모든 작업이 재구성되었으므로 필터링 f된 출력을 얻습니다.

보시다시피, Awk 패러다임은 기본적으로 완전합니다. 단지 다른 언어의 맥락에서 다른 종류의 일이 가능하다는 것뿐입니다. 필드가 실제로 존재하는지 확인하지 않고 작동할 수 있는 편리함은 $2 > $1없지만 반면에 필드를 데이터 구조로 처리하기 위해 루프를 작성할 필요가 없습니다. 필드는 함수를 통해 매핑되거나 스택으로 표시될 수 있습니다.

Sundeep의 Perl 솔루션은 대략 awk다음 매크로로 변환됩니다.

$ txr -e '(awk (t (prn `@[f 0]\t@{(keep-if #/cow|dog/ [f 1..:]) "\t"}`)))' data

관련 정보