사전 기반 검색 및 바꾸기를 수행하려고 하는데 대소문자를 구분/정확하게 일치시키는 방법을 알 수 없지만 매우 어려운 것으로 판명되었습니다.
세 개의 파일이 있는데, fileA는 편집할 텍스트, FileB는 검색할 단어 목록, FileC는 바꿀 단어 목록입니다.
paste -ds///g /dev/null /dev/null <(sed 's|[[\.*^\b$\b/]|\\&|g' fileB) <(sed 's|[\&/]|\\\b&\b|g' fileC) /dev/null /dev/null | sed -f - fileA
내가 아는 한, sed에서 정확한 일치 항목을 검색하고 바꾸려면 다음과 같은 작업을 수행해야 합니다.sed 's/\<exact_word_to_replace\>/exact_replacement/g' filename
\<
하지만 위 코드의 어디에 있는지 잘 모르겠습니다. \>
가야 합니다!
더 좋아 질까 \b
? 그렇다면 그건 어디로 가는 걸까요?
누군가가 나를 올바른 방향으로 밀어줄 수 있기를 바랍니다...
건배, 니오븀
이는 다음을 기반으로 합니다. https://unix.stackexchange.com/a/271108
답변1
나는 paste
이것을 전혀 사용하지 않을 것입니다. sed
나는 awk나 perl을 사용할 것이다. 예를 들어:
먼저, 일부 샘플 입력 파일입니다. (나의 편의를 위해) File[ABC]
파일 A와 B는 검색 패턴과 해당 대체 항목의 의미를 변경했음을 참고하세요. FileC는 수정할 입력 텍스트 파일입니다.
중요한 것은 검색어가 포함된 파일이 스크립트의 첫 번째 인수이고 대체 문자열이 포함된 파일이 두 번째 인수라는 것입니다. 수정될 실제 입력은 세 번째(있는 경우 후속) 인수 및/또는 표준 입력에서 옵니다.
$ cat FileA
house
$ cat FileB
dwelling
$ cat FileC
Mr House does not live in a land-based house, his house is a houseboat.
Perl 스크립트도 있습니다. 다른 이름으로 저장 replace.pl
하고 실행 가능하게 만듭니다 chmod +x replace.pl
.
$ cat replace.pl
#!/usr/bin/perl
use strict;
# Variables to hold the first two filenames.
my $FileA = shift;
my $FileB = shift;
# An associative array ("hash") called %RE. The keys are the search
# regexes and the values are the replacements.
my %RE;
# Read both FileA and FileB at the same time, to build a
# hash of pre-compiled regular expressions (%RE) and their
# replacements.
open(my $A,'<',$FileA) || die "Couldn't open $FileA for read: $!\n";
open(my $B,'<',$FileB) || die "Couldn't open $FileB for read: $!\n";
while(my $a = <$A>) { # loop reading lines from first file
die "$FileA is longer than $FileB" if (eof $B);
my $b = <$B>; # read in a line from 2nd file
die "$FileB is longer than $FileA" if (eof $A && ! eof $B);
chomp($a,$b);
# Uncomment only ONE of the following four lines:
$RE{qr/\b$a\b/} = $b; # regular expression match
#$RE{qr/\b\Q$a\E\b/} = $b; # exact-match version.
#$RE{qr/(?<!-)\b$a\b(?!-)/} = $b; # regexp match, no hyphen allowed
#$RE{qr/(?<!-)\b\Q$a\E\b(?!-)/} = $b; # exact match, no hyphen allowed.
}
close($A);
close($B);
# process stdin and/or any remaining filename argument(s) on
# the command line (e.g. FileC).
while (<>) {
foreach my $a (keys %RE) {
s/$a/$RE{$a}/g;
};
print;
}
노트:
Perl의
chomp
기능은 변수 또는 변수 목록에서 후행 입력 레코드 구분 기호($/
-텍스트 파일 유형 및 운영 체제에 따라 개행 또는 CR+LF와 같은 줄 끝 문자)를 제거합니다. 바라보다perldoc -f chomp
.Perl의
qr
참조 연산자는 컴파일된 정규식을 반환합니다.perldoc -f qr
자세히보다.검색, 바꾸기 및 텍스트 파일이 모두 작은 경우 미리 컴파일된 정규식은 별 차이가 없습니다. 검색 및 바꾸기 목록(파일 A 및 B)이 길거나 입력(파일 C)이 큰 경우 성능에 큰 차이가 있습니다. 정규식을 반복적으로 컴파일하는 오버헤드로 인해 CPU 처리 능력과 시간이 크게 소모됩니다.
정규식은 FileA에서 컴파일되므로
\b$a\b
FileA의 값 주위에 너비가 0인 단어 경계 표시가 포함되어 있습니다. 보기man perlre
및 검색word boundary
"너비 없음"은\b
실제로 입력 텍스트를 일치시키거나 사용하지 않고 거기에서 볼 것으로 예상되는 것만 주장하는 것을 의미합니다. 너비가 0인 어설션의 다른 예로는^
(Row Anchor Start) 및$
(Row Anchor End)가 있습니다.Assertions
동일한 매뉴얼 페이지 내에서 검색하십시오.FileA의 패턴을 고정 문자열로 처리하려면(즉, 모든 정규식 메타 문자가 특별한 의미가 없는 리터럴 문자열
*
로 처리됨 ) 패턴을 및로 묶어 메타 문자를 비활성화(따옴표)하십시오. 중요한 것은 무엇인가?
\Q
\E
\b
외부의\Q
및\E
. 주석 처리된 예제를 추가했습니다. 이는 에도 문서화되어 있습니다man perlre
.FileA의 패턴이
\
이스케이프되지 않은 문자로 끝나면 스크립트가 중단됩니다. 또한\E
고정 문자열 버전을 사용하는 경우 포함된 패턴으로 인해 버전이 손상될 수 있습니다. 또한\Q
고정되지 않은 문자열 버전에서도 문제가 발생할 수 있습니다. 쓰레기는 들어가고 쓰레기는 나옵니다. 입력 내용을 정리하세요.또한
man perlre
: Perl에서는 문자(\w
)라는 단어를 다음과 같이 정의합니다.영숫자 문자와 "_", 기타 연결 구두점 문자 및 유니코드 태그하이픈과 대부분의 기타 구두점 문자는 단어를 끝냅니다.
houseboat
FileC의 내용은 동일하게 유지되지만 로house-boat
변경되며dwelling-boat
이는 이상적이지 않습니다.share-house
share-dwelling
이 문제는 RE의 하이픈 문자(예: 또는 )에 대해 너비가 0인 부정 예측 및 뒤돌아보기 어설션(각각 및 )을 사용하도록 스크립트를 변경하여
(?!pattern)
해결할 수 있습니다. 간단히 말해서, 이는 Perl의 정규식 엔진에 "우리가 찾고 있는 패턴이 그 앞이나 뒤에 존재한다면 일치하지 않습니다."라고 알려줍니다.(?<!pattern)
$RE{qr/(?<!-)\b$a\b(?!-)/} = $b;
$RE{qr/(?<!-)\b\Q$a\E\b(?!-)/} = $b;
-
RE가 다음 문자를 집어삼키는 것을 방지하려면 여기서 너비가 0인 어설션을 사용하는 것이 중요합니다(그렇게 부정된 문자 클래스뿐만 아니라
[^-]
)(같은 이유로 너비가 0인 어설션은\b
실제로 입력을 일치하거나 소비하지 않습니다). 이번에도 로그인이 되어man perlre
검색을 해보세요Lookaround Assertions
.이 예제도 스크립트에 추가했습니다.
수정자는 사용되지 않으므로
/i
정규식 일치는 대소문자를 구분합니다.스크립트에는 매우 원시적인 매개변수 처리 기능이 있습니다. 더 나은 것이 필요하다면 Perl의 많은 명령줄 인수/옵션 처리 모듈 중 하나를 사용하십시오.GetSelect::표준또는Getopt::긴. 이는 핵심 Perl 모듈이며 Perl에 포함되어 있습니다.
마지막으로 일부 샘플 출력은 다음과 같습니다.
$ ./replace.pl FileA FileB FileC
Mr House does not live in a land-based dwelling, his dwelling is a houseboat.
스크립트가 실제로 각 개별 입력 파일을 변경하도록 하려면(단순히 표준 출력으로 인쇄하는 대신) 첫 번째 줄을 다음과 같이 변경합니다.
#!/usr/bin/perl
도착하다
#!/usr/bin/perl -i
또는 (원본 파일을 .bak로 저장하려는 경우):
#!/usr/bin/perl -i.bak
그런데 -i
내부 편집 옵션을 사용하더라도 입력이 파일 대신 표준 입력에서 오는 경우 스크립트는 계속 작동합니다.