awk를 사용하여 첫 번째 행에 "-"가 있는 열을 삭제합니다.

awk를 사용하여 첫 번째 행에 "-"가 있는 열을 삭제합니다.

테이블이 있어요

M       -       A       A       -
-       A       G       -       -
M       -       -       -       G

나는 다음을 실행하고 싶다:첫 번째 행의 열에 "-"가 포함되어 있으면 해당 열 인쇄를 건너뜁니다.

예상 출력은 다음과 같습니다.

M       A       A 
-       G       - 
M       -       - 

나는 성공하지 못한 채 이와 같은 것을 시도했습니다.

awk 'NR==1 && $i!="-" {print $i}'

명령을 수정하는 방법을 아는 사람이 있습니까?

답변1

의 변종에드 모튼의 답변는 필드 번호별로 첫 번째 행에 없는 필드를 기억한 다음 새 레코드를 인쇄하기 전에 -배열에 저장된 인덱스를 기반으로 입력의 각 레코드를 다시 구성합니다 .out

FNR == 1 {
    for (i = 1; i <= NF; ++i)
        if ($i != "-") out[++nf] = i
}

{
    for (i = 1; i <= nf; ++i)
        a[i] = $(out[i])

    $0 = ""

    for (i = 1; i <= nf; ++i)
        $i = a[i]

    print
}

여기서는 가독성을 위해 약간의 효율성을 희생하고 두 번째 블록의 단일 루프에서 필수 필드를 인쇄하는 대신 별도의 루프에서 레코드를 재구성했습니다.

시험:

$ awk -f script.awk file
M A A
- G -
M - -

탭을 출력 필드 구분 기호로 사용하여 실행합니다.

$ awk -v OFS='\t' -f script.awk file
M       A       A
-       G       -
M       -       -

입력 데이터가 탭으로 구분되어 있는지 여부에 따라 코드 한 줄이 약간 너무 깁니다.

$ cut -f "$(awk -v OFS=',' '{ nf=split($0,a); $0=""; for (i=1; i<=nf; ++i) if (a[i]!="-") $(++NF)=i; print; exit }' file)" file
M       A       A
-       G       -
M       -       -

awk이는 출력하는 데 사용됩니다.필드 번호-쉼표로 구분된 목록으로 첫 번째 줄 에 없습니다 . 그런 다음 해당 목록은 목록으로 전달되어 cut -f실제로 파일의 데이터를 출력합니다. 파일 이름(여기서 축약됨 file)은 명령줄에 두 번 제공됩니다. 한 번은 에 대해, awk또 한 번은 에 대해 다시 제공됩니다 cut.

답변2

$ cat tst.awk
NR == 1 {
    for (i=1; i<=NF; i++) {
        if ($i != "-") {
            f[++numOutFlds] = i
        }
    }
}
{
    for (i=1; i<=numOutFlds; i++) {
        printf "%s%s", $(f[i]), (i<numOutFlds ? OFS : ORS)
    }
}

$ awk -f tst.awk file
M A A
- G -
M - -

답변3

awk입력 레코드(행) 및 파일에 대해 암시적으로 루프를 수행하지만 명시적으로 수행해야 하는 필드에 대해서는 루프를 수행하지 않습니다. 귀하의 경우 첫 번째 행(헤더 행)의 필드를 반복하여 포함할 열을 결정한 다음 반복해야 합니다.모든행(제목 및 비제목)에 해당 행에 필요한 열을 포함합니다.

다음 헤더 필드를 찾고 있는지 확실하지 않습니다.동일한(문자열) "-" 또는 다음과 같이 전달할 수도 있습니다.(하위)문자열. 또한 필드 구분 기호로 여러 개의 공백이 아닌 단일 탭이 있다고 가정합니다. 이는 더 지루하며 게시물과 시각적으로 구별되지 않습니다.

awk -F"\t" 'NR==1{for(i=1;i<=NF;i++)s[i]=$i!="-"} {x="";for(i=1;i<=NF;i++)if(s[i])x=x FS $i;print substr(x,2)}'
# for _matches_ "-" instead of _equals_ "-" change $i!="-" to $i!~/-/
# note if a nonheader line has more fields than the header did,
# all extra fields are nonselected (as if their header field was/matched -)

# or (re)use the flags for both what to include _and_ when to terminate the line
awk -F"\t" 'NR==1{t=RS;for(i=NF;i;i--)if(s[i]=($i!="-"?t:""))t=FS} {for(i=1;i<=NF;i++)if(s[i])printf "%s%s",$i,s[i]}'
# some people may consider this too clever

답변4

를 사용하여 이 작업을 수행할 수 있습니다 sed. 비록 코드가 확장 정규식 모드에서 GNU sed를 사용하고 있지만 이것은 단지 치료법일 뿐입니다 backslashitis.

방법은 첫 번째 행부터 지도를 생성하는 것입니다. 유지될 필드는 x에 매핑되고 다른 필드는 대시에 매핑됩니다. 이 지도를 화물칸에 보관하세요.

그런 다음 모든 라인에 대해 이 지도를 추가하고 BOL에 마커를 배치합니다.

루프에서 \n-이 표시되고 마커가 다음 필드로 이동하면 현재 줄의 선행 필드가 계속 제거됩니다.

이 마커가 현재 줄과 예약된 공간 사이의 개행 문자와 충돌하면 루프가 종료됩니다(G 명령으로 인해).

$ sed -Ee '
    1{
      h
      y/-/\n/
      s/\S+/x/g;s/[[:blank:]]+//g
      y/\n/-/
      x
    }

    G;s/^/\n/

    :a
      s/\n(\S+\s*)(.*\n)x/\1\n\2/
      s/\n(\S+\s*)(.*\n)-/\n\2/
    /\n\n/!ba

    s/\s+$//
' file

결과

M       A       A
-       G       -
M       -       -

관련 정보