파일의 각 행에 대해 해당 값이 다른 필드의 값보다 작으면 특정 열의 필드를 NF로 인쇄합니다.

파일의 각 행에 대해 해당 값이 다른 필드의 값보다 작으면 특정 열의 필드를 NF로 인쇄합니다.

줄당 필드 수가 가변적인 다음 형식의 파일이 있습니다.

NC_000001.11_NM_001005484.2 69270   234 69037   65565   69037
NC_000001.11_NM_001005484.2 69511   475 69037   65565   69037
NC_000001.11_NM_001005484.2 69761   725 69037   65565   69037
NC_000001.11_NM_001385640.1 942155  20  942136  924432  925922  930155  931039  935772 939040   939272  941144  942136  942410  942559  943253  943698  943908  

각 행에 대해 처음 4개 필드를 인쇄하고 싶습니다. 나머지 필드($5 ~ NF)의 경우 이 필드의 값이 $4의 값보다 작으면 해당 필드를 인쇄하고 싶습니다.

출력 예:

NC_000001.11_NM_001005484.2 69270   234 69037   65565   
NC_000001.11_NM_001005484.2 69511   475 69037   65565   
NC_000001.11_NM_001005484.2 69761   725 69037   65565   
NC_000001.11_NM_001385640.1 942155  20  942136  924432  925922  930155  931039  935772 939040   939272  941144  

여러 가지 awk 옵션을 시도했지만 모두 실패했습니다. awk를 처음 접했고 도움을 주시면 감사하겠습니다.

답변1

출력에서 공백을 신경 쓰지 않는다면 필요한 것은 다음과 같습니다.

$ cat tst.awk
{
    out = $1 OFS $2 OFS $3 OFS $4
    for (i=5; i<=NF; i++) {
        if ( $i < $4 ) {
            out = out OFS $i
        }
    }
    print out
}

$ awk -f tst.awk file
NC_000001.11_NM_001005484.2 69270 234 69037 65565
NC_000001.11_NM_001005484.2 69511 475 69037 65565
NC_000001.11_NM_001005484.2 69761 725 69037 65565
NC_000001.11_NM_001385640.1 942155 20 942136 924432 925922 930155 931039 935772 939040 939272 941144

column원하는 경우 파이프를 통해 시각적 정렬을 수행할 수 있습니다.

$ awk -f tst.awk file | column -t
NC_000001.11_NM_001005484.2  69270   234  69037   65565
NC_000001.11_NM_001005484.2  69511   475  69037   65565
NC_000001.11_NM_001005484.2  69761   725  69037   65565
NC_000001.11_NM_001385640.1  942155  20   942136  924432  925922  930155  931039  935772  939040  939272  941144

그렇지 않으면 출력의 간격이 입력의 간격처럼 보이길 원하는 경우(즉, 처음 4개 필드는 1개 이상의 공백으로 보이고 나머지는 2개 이상의 공백으로 표시됨) 일부 줄에는 4개 이하의 공백만 있을 수 있다고 가정합니다. 필드를 선택한 다음 POSIX awk를 사용합니다(문자 클래스 및 정규식 공백용).

$ cat tst.awk
BEGIN { OFS="\t" }
match($0,/([^[:space:]]+[[:space:]]+){3}[^[:space:]]+/) {
    out = substr($0,RSTART,RLENGTH)
    for (i=5; i<=NF; i++) {
        if ( $i < $4 ) {
            out = out OFS $i
        }
    }
    $0 = out
}
{ print }

$4 이후의 필드를 탭으로 구분해야 하는 경우:

$ awk -f tst.awk file
NC_000001.11_NM_001005484.2 69270   234 69037   65565
NC_000001.11_NM_001005484.2 69511   475 69037   65565
NC_000001.11_NM_001005484.2 69761   725 69037   65565
NC_000001.11_NM_001385640.1 942155  20  942136  924432  925922  930155  931039  935772  939040  939272  941144

또는 공백으로 구분해야 하는 경우:

$ awk -f tst.awk file | column -s$'\t' -t
NC_000001.11_NM_001005484.2 69270   234 69037   65565
NC_000001.11_NM_001005484.2 69511   475 69037   65565
NC_000001.11_NM_001005484.2 69761   725 69037   65565
NC_000001.11_NM_001385640.1 942155  20  942136  924432  925922  930155  931039  935772  939040  939272  941144

위의 내용은 입력의 탭 및/또는 공백 조합에 대해 처음 4개 필드 사이의 공백을 유지한 다음 매 5번째 및 후속 필드 앞에 탭을 인쇄합니다. 이를 사용하여 column동등 항목을 원하는 경우 비워 둘 수 있습니다. 둘 다 질문의 입력 및 출력처럼 보입니다.

out위의 루프에서 이름이 지정된 새 문자열을 작성하고 루프를 수정 하거나 루프 내부에서 수정하는 $0대신 루프 뒤에 한 번 할당합니다 . 왜냐하면 awk를 변경할 때마다 해당 필드에서 다시 빌드해야 하고 awk를 변경할 때마다 다시 분할해야 하기 때문입니다. 필드에 포함하므로 둘 다 비효율적이며 필드 내용에 따라 예기치 않은 오류가 발생할 수 있으므로 매우 특정한 목적이 없는 한 루프 내에서 수정하거나 수정 해서는 안 됩니다.$0$i$i$0$0$0$0$i

답변2

이는 GNU Awk 5.1.0, API: 3.0을 사용하여 테스트되었습니다. split이 솔루션의 네 번째 매개변수를 사용하면 여기에 사용된 구문과 호환되지 않는 다른 버전에서는 작동하지 않을 수 있기 때문입니다.

awk '{n=split($0, a, " ", b); line=""; for (i = 1; i <= n; i++) { if (i < 5 || a[i] < $4) line=(line a[i] b[i])}; print line; }' file.txt

설명하다:

  • n=split($0, a, " ", b);- 전체 줄( $0)을 값( 에 저장됨 a)과 공백( 에 저장됨 b)으로 분할하므로 원본 파일의 형식을 보존할 수 있습니다. 저장된 값은 n각 행에 대해 처리할 필드 수를 제공합니다. split배열 ab인덱스는 모두 1부터 시작합니다.
  • line=""- 빈 문자열로 시작
  • for (i = 1; i <= n; i++)- 각 필드를 반복하고 인덱스 1부터 분할하여 루프를 만듭니다. <=마지막(n번째) 필드도 처리되도록 부분적으로 보장합니다.
  • if (i < 5 || a[i] < $4)- 처음 4개 필드 또는 필드 값이 4번째 필드(원하는 조건)보다 작을 때 조건이 true입니다.
  • line=(line a[i] b[i])- "if" 조건 요구 사항을 충족하는 이전 필드 및 공간과 실제 필드 및 공간을 연결합니다.
  • print line- line원하는 출력이 포함된 변수를 인쇄합니다.

답변3

이는 줄 끝에서 줄 시작까지(즉, 역순으로) 필드를 반복하고 NF필드 번호( )가 4보다 큰 경우 필드를 제거합니다.그리고이 필드의 값은 $4필드 4( )의 값보다 큽니다.

$ awk '{
    for (i=NF; i>=1; i--) {
      if ((i > 4) && ($i >= $4)) {
        $i=""
      }
    };
    print
    }' input.txt
NC_000001.11_NM_001005484.2 69270 234 69037 65565 
NC_000001.11_NM_001005484.2 69511 475 69037 65565 
NC_000001.11_NM_001005484.2 69761 725 69037 65565 
NC_000001.11_NM_001385640.1 942155 20 942136 924432 925922 930155 931039 935772 939040 939272 941144 

그런데 입력 내용이 공백인지 탭으로 구분되어 있는지 확실하지 않습니다. 각 필드 사이에 공백 대신 탭으로 구분된 출력을 원하는 경우 -v OFS='\t'스크립트를 시작하는 작은따옴표 앞에 awk 명령을 추가하세요. 예를 들어

awk -v OFS='\t' '...awk script here...' input.txt

그런데 awk는 필드가 제거되기 전의 위치에 관계없이 출력 줄에 많은 추가 필드 구분 기호를 남깁니다. 이를 제거하려면 명령문 앞에 다음 행을 추가하십시오 print.

    $0=$0; $1=$1;

이렇게 하면 awk가 입력 행을 재평가하고 이를 필드로 다시 분할하도록 강제하여 빈 필드를 효과적으로 제거합니다(FS에서 분할, 필드 구분 기호, 기본값은 공백 수에 제한 없음). awk는 실제로 행의 필드를 삭제할 수 있는 방법이 없으므로 행을 수정한 후에 강제로 삭제해야 하기 때문에 이것은 약간의 해킹입니다.

관련 정보