대용량 ASCII 텍스트 파일에서 검색/바꾸기 기능을 실행해야 합니다. 입력 파일에서 짧은 발췌:
gene_id "MSTRG.1";
gene_id "MSTRG.1";
gene_id "MSTRG.2";
gene_id "MSTRG.3";
이 MSTRG
문자열은 템플릿 파일에 있는 다른 ID로 대체됩니다.
MSTRG.1 AT1G01030
MSTRG.2 AT1G01010
MSTRG.3 AT1G01035
간단한 while 루프는 템플릿의 각 줄을 반복하고 대체합니다.
while read bef aft
do
echo "Searching for $bef"
echo "Replacing with $aft"
sed "s/$bef/$aft/g" input > output
done < template
MSTRG.2
이후 항목이 올바르게 대체되지만 변경되지 않은 상태 로 유지됩니다 MSTRG.1
. 출력은 다음과 같습니다.
gene_id "MSTRG.1";
gene_id "MSTRG.1";
gene_id "AT1G01010";
gene_id "AT1G01035";
고쳐 쓰다
이것이 내가 한 일입니다.
while read bef aft
do
sed -i "s/$bef/$aft/g" input
done < template
답변1
문제는 루프를 반복할 때마다 출력 파일을 삭제하여 가장 최근 변경 사항만 남기고 output
이전 변경 사항은 남기지 않는다는 것입니다.
대신 template
파일을 일련의 sed
명령으로 쉽게 변환할 수 있습니다.
$ awk '{ printf("s/%s/%s/g\n", $1, $2) }' template
s/MSTRG.1/AT1G01030/g
s/MSTRG.2/AT1G01010/g
s/MSTRG.3/AT1G01035/g
...그런 다음 파일에 적용합니다.
$ awk '{ printf("s/%s/%s/g\n", $1, $2) }' template | sed -f - input
gene_id "AT1G01030";
gene_id "AT1G01030";
gene_id "AT1G01010";
gene_id "AT1G01035";
일부 구현에서는 표준 입력의 의미를 sed
인식하지 못합니다 . -
이 유형의 에 이 방법을 사용하려면 로 sed
바꾸십시오 .-f -
-f /dev/stdin
또는 다음에서 모든 작업을 수행할 수 있습니다 awk
.
$ awk 'FNR == NR { pat[$1] = $2; next } { for (p in pat) gsub(p, pat[p]); print }' template input
gene_id "AT1G01030";
gene_id "AT1G01030";
gene_id "AT1G01010";
gene_id "AT1G01035";
위의 모든 변형은 첫 번째 열의 내용을 다음 template
과 같이 사용합니다.정규식, 의미 .
(점)이 일치함어느특징.
답변2
각 루프 반복에서 출력 파일을 덮어쓰는 대신 입력 파일을 출력 파일로 복사하고 이 출력 파일에서 작업할 수 있습니다.
sed
옵션 변경 사항 이 -i
동일한 파일에 기록되므로 이전 대체 항목이 손실되지 않습니다.
cp input output
while read bef aft
do
echo "Searching for $bef"
echo "Replacing with $aft"
sed -i "s/$bef/$aft/g" output
done < template
답변3
#!/usr/bin/perl -i
use strict;
# The %re hash holds the regexp searches and replacement strings.
my %re = ();
my $tfile = shift;
open(TEMPLATE, "<", $tfile) || die "couldn't open $tfile for read: $!\n";
while(<TEMPLATE>) {
chomp;
my ($search,$replace) = split;
$re{qr/$search/} = $replace;
};
close(TEMPLATE);
while (<>) {
foreach my $s (keys %re) {
s/$s/$re{$s}/g;
};
print;
}
이는 template
파일을 읽고 %re
정규식 검색 및 바꾸기라는 연관 배열(일명 "해시")을 구축합니다.
그런 다음 명령줄(예: input
)에 남아 있는 각 파일 이름을 반복하고 각 입력 줄에서 검색 및 바꾸기 작업을 모두 수행합니다. 정규식을 미리 컴파일하는 데 사용됩니다 qr//
. 줄이 많지 않으면 이는 사소한 최적화일 뿐이지만 template
줄이 많으면 속도가 크게 향상될 수 있습니다.
이 -i
줄 #!/usr/bin/perl -i
은 변경 사항을 표준 출력으로 인쇄하는 대신 Perl이 입력 파일의 내부 편집을 수행하도록 합니다. 예를 들어, -i.bak
파일이 변경되기 전에 파일의 백업 복사본을 보관하려면 로 변경하세요.
예를 들어 다른 이름으로 저장하여 cryptic0.pl
실행 가능하게 만들고 chmod +x cryptic0.pl
다음과 같이 실행하십시오.
$ ./cryptic0.pl template input
스크립트는 터미널에 어떤 출력도 생성하지 않습니다. 대신 입력 파일을 편집합니다.
예를 들어 input
파일은 다음과 같이 변경됩니다.
$ cat input
gene_id "AT1G01030";
gene_id "AT1G01030";
gene_id "AT1G01010";
gene_id "AT1G01035";
그런데, 이 스크립트는 모든 줄의 모든 항목을 적절한 대체 문자열로 변경합니다. 당신이 확신한다면 당신은 단지 가질 수 있습니다하나주어진 줄과 일치하면 다음 줄을 변경하여 작업 속도를 높일 수 있습니다.
s/$s/$re{$s}/g;
이와 관련하여:
s/$s/$re{$s}/ && last;
이로 인해 스크립트는 foreach 루프에서 해당 문으로 이동한 print
후 성공적인 검색 및 바꾸기 후에 즉시 다음 입력 줄로 이동합니다.
그건 그렇고, 참조하십시오쉘 루프를 사용하여 텍스트를 처리하는 것이 왜 나쁜 습관으로 간주됩니까?텍스트 처리에 sh 루프를 사용하는 것이 왜 나쁜 생각입니까? 또는 대신 또는 또는 또는 다른 awk
것을 perl
사용 하십시오 .sed
sh
bash