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에 탭으로 분할하도록 지시합니다. 또한 , 및 을 보고 검색 할 수 있는-F
Perl 모드가 열립니다 .-n
sed -n
man perlrun
-F
-a
-n
Perl의 배열 인덱싱은 0부터 시작하므로
$F[0]
첫 번째 요소는 0부터 시작하고$F[1]
두 번째 요소도 0부터 시작하는 식입니다.$,
Perl의 출력 필드 구분 변수입니다(perlvar
man 페이지에 설명되어 있음). 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 버전에는 모두 동일한 기본 문제가 있습니다. 즉, 두 번째 필드의 내용에 따라 가변 개수의 필드도 출력한다는 것입니다. 문제를 해결하는 것은 어렵지 않습니다.