awk 명령이 열 2에서 작동하도록 어떻게 지시할 수 있나요?

awk 명령이 열 2에서 작동하도록 어떻게 지시할 수 있나요?

awk이 명령을 사용하여 열 2의 마지막 밑줄을 탭으로 바꾸길 원합니다 . 이제 각 줄의 마지막 밑줄이 탭 문자로 대체됩니다. 각 줄 열의 밑줄 수가 다를 수 있다는 점에 유의하세요. 나는 명령이 열 2에서만 작동하도록 지시하기 위해 여러 가지 방법을 시도했습니다. 꽤 가까워졌는데 최종 조정을 해줄 수 있는 사람이 있나요?

탭으로 구분된 예제 파일:

OTU1 this_is_the_second_column 100 0 450 this_is_the_sixth_column 1 5 3.2
OTU2 this_is_another_column_to_parse 103 4 650 this_is_another_test_string_too 4 7 4.6

다음과 같아야 합니다.

OTU1 this_is_the_second column 100 0 450 this_is_the_sixth_column 1 5 3.2
OTU2 this_is_another_column_to parse 103 4 650 this_is_another_test_string_too 4 7 4.6

이것은 내 현재 코드입니다.

gawk -F'\t' -v OFS='\t' 'BEGIN{FS=OFS="_"}{last=$NF;NF--;print $0"\t"last}' test1.tab > test1_reformat.tab

어떤 도움이라도 대단히 감사하겠습니다.

감사해요

답변1

GNU awk가 있는 것 같으므로 이것을 사용할 수 있습니다.뿌리이 함수는 밑줄 뒤의 밑줄이 아닌 후행 시퀀스를 캡처하고 탭 뒤에서 다시 바꿉니다.

gawk 'BEGIN {OFS=FS="\t"} {$2 = gensub(/_([^_]*)$/, "\t\\1", "1", $2)} 1' test1.tab

또는 (내 생각에는 이식 가능) 이 match함수를 사용하여 문자열 분할을 수행할 수 있습니다.

awk 'BEGIN{OFS=FS="\t"} match($2,/_[^_]*$/) {$2 = substr($2,1,RSTART-1) "\t" substr($2,RSTART+1)} 1' test1.tab

답변2

여기요

gawk '
    BEGIN { OFS = FS = "\t" }              # Output as input as tabs
    {
        n = split($2, a, "_");             # Split $2 by "_" into array
        for(i = 1; i<n; i++) {
            s = (i>1 ? s "_" : "") a[i]    # Rejoin fields with "_"
        }
        $2 = s OFS a[n];                   # Join last with OFS
        print
    }
' file

주석을 제거하면 한 줄에서 실행할 수 있지만 프로덕션 코드에서는 이 작업을 수행하지 않는 것이 좋습니다.

입력 예에 대한 출력(여기서 형식 제한으로 인해 탭이 공백으로 확장됨)

OTU1    this_is_the_second      column  100     0       450     this_is_the_sixth_column        1       5       3.2
OTU2    this_is_another_column_to       parse   103     4       650     this_is_another_test_string_too 4       7       4.6

답변3

솔루션을 지나치게 복잡하게 만들지 마십시오. 원하는 것을 달성하는 한 가지 방법은 다음과 같습니다.

입력 내용이 다음과 같고 저장되어 있다고 가정합니다 infile(데모 목적으로 다른 줄을 추가합니다).

OTU1 this_is_the_second_column 100 0 450 this_is_the_sixth_column 1 5 3.2
OTU2 this_is_another_column_to_parse 103 4 650 this_is_another_test_string_too 4 7 4.6
OTU2 this_is_another_column_to_parse_and_parse 103 4 650 this_is_another_test_string_too 4 7 4.6

그러면 다음과 같이 할 수 있습니다:

awk -vOFS="\t" '{p = match($2, /_[^_]*$/); if (p) $2 = substr($2, 1, p-1) "\t" substr($2, p+1)}1' infile

산출:

OTU1    this_is_the_second  column  100 0   450 this_is_the_sixth_column    1   5   3.2
OTU2    this_is_another_column_to   parse   103 4   650 this_is_another_test_string_too 4   7   4.6
OTU2    this_is_another_column_to_parse_and parse   103 4   650 this_is_another_test_string_too 4   7   4.6

답변4

펄 사용:

$ perl -F"\t" -le 'BEGIN{ $, = "\t" };
                   $F[1] =~ s/^(.*)_(.*)/$1$,$2/;
                   print @F' test1.tab  
OTU1    this_is_the_second      column  100     0       450     this_is_the_sixth_column        1       5       3.2
OTU2    this_is_another_column_to       parse   103     4       650     this_is_another_test_string_too 4       7       4.6
  • -F"\t"Perl의 자동 분할 모드를 켜고(awk와 유사하지만 @F$1, $2, $3 등 대신 명명된 배열을 사용함) Perl에 탭으로 분할하도록 지시합니다. 또한 , 및 을 보고 검색 할 수 있는 -FPerl 모드가 열립니다 .-nsed -nman perlrun-F-a-n

    Perl의 배열 인덱싱은 0부터 시작하므로 $F[0]첫 번째 요소는 0부터 시작하고 $F[1]두 번째 요소도 0부터 시작하는 식입니다.

  • $,Perl의 출력 필드 구분 변수입니다( perlvarman 페이지에 설명되어 있음). BEGIN 블록( BEGIN{ $, = "\t" })에 설정하면 각 입력 줄에 대해 한 번이 아니라 스크립트가 시작될 때 한 번만 실행됩니다.

  • $F[1] =~ s/^(.*)_(.*)/$1$,$2/_두 번째 필드의 마지막 항목을 로 변경합니다 $,. Perl의 정규식 일치는 기본적으로 탐욕적이므로 ^(.*)_이전의 모든 항목이 일치되고 캡처됩니다.마지막 _.

  • @F그런 다음 배열을 인쇄하십시오.

이것은 Perl 버전이 설치되어 있으면 작동한다는 점에서 이식 가능합니다(즉, GNU awk for 와 같은 비표준 버전이 필요하지 않습니다 gensub()).


또는 설정 대신 Perl의 join()기능( 참조 )을 사용하십시오 .perldoc -f join$,

perl -F"\t" -le '$F[1] =~ s/^(.*)_(.*)/$1\t$2/;
                 print join "\t", @F' test1.tab

대안은 이 splice()함수(참고자료 참조 perldoc -f splice)를 사용하여 실제로 필드 2와 3 사이의 배열에 새 요소를 삽입하는 것입니다 @F(필드 구분 기호를 포함하는 두 번째 요소와 반대). 새로운 요소는 세 번째 요소( $F[2])가 되며, 이후의 모든 요소의 인덱스는 1씩 증가합니다.

이는 새 필드를 삽입한 후 배열에서 추가 처리를 수행해야 하는 경우 유용합니다(awk와 달리 Perl에는 배열 조작 기능이 내장되어 있으므로 배열에서 요소를 삽입하거나 제거하는 것이 간단합니다).

perl -F"\t" -le '$F[1] =~ s/^(.*)_(.*)/$1/;
                 splice @F, 2, 0, $2;
                 print join "\t", @F' test1.tab

주목할 만한 점: 대체 연산자에서 그룹을 캡처하는 것은 해당 그룹이 s///범위를 벗어나거나 다른 정규식이 일치하거나 대체가 성공할 때까지 지속됩니다. 이것이 바로 $2와 함께 사용할 수 있는 이유입니다 splice.

$F[1]이는 또한 이 버전이 입력 라인에서 제대로 작동하지 않음을 의미합니다.아니요문자를 포함합니다 _(마지막으로 성공한 일치 항목에서 $2가 포함된 세 번째 필드를 삽입하거나 첫 번째 대체가 성공할 때까지 빈 필드를 삽입합니다). 이를 처리하려면 교체가 성공했는지 테스트해야 합니다. 예를 들면 다음과 같습니다.

perl  -F"\t" -le 'if ($F[1] =~ s/^(.*)_(.*)/$1/) {
                    splice @F, 2, 0, $2;
                  } else {
                    splice @F, 2, 0, ""; # insert empty field 3
                  };
                  print join "\t", @F' test1.tab

다른 버전도 실제로는 제대로 작동하지 않습니다. 탭으로 구분된 다양한 필드 수(두 번째 필드에 밑줄이 포함된 경우 10개, 그렇지 않은 경우 9개)가 포함된 행을 출력합니다.

1. 두 번째 필드에 항상 하나 이상의 밑줄이 포함되는 것이 보장되는 경우 또는 2. 출력에 필드가 9개인지 10개인지 상관하지 않습니다.

교체가 실패하면 두 번째 필드에 필드 구분 기호를 추가하여 이러한 버전을 수정할 수 있습니다. 예를 들어 아래와 같습니다.

$F[1] .= $, unless $F[1] =~ s/^(.*)_(.*)/$1$,$2/;

아니면 이거:

$F[1] .= "\t" unless $F[1] =~ s/^(.*)_(.*)/$1\t$2/;

그런데 지금까지 다른 답변의 awk 버전에는 모두 동일한 기본 문제가 있습니다. 즉, 두 번째 필드의 내용에 따라 가변 개수의 필드도 출력한다는 것입니다. 문제를 해결하는 것은 어렵지 않습니다.

관련 정보