다음에서 문자열을 바꾸려고 합니다 file A
.
Hello Peter, how is your dad? where is mom?
교체할 문자열은 다음 위치에 있습니다 file B
.
Peter
dad
mom
해당 대체 항목은 다음 위치에 있습니다 file C
.
John
wife
grandpa
예상되는 결과:
Hello John, how is your wife? where is grandpa?
의 값을 의 해당 행 값으로 편집하고 바꿀 수 있습니까 file A
?file B
file C
지금까지 내가 한 일:
cat 1.txt | sed -e "s/$(sed 's:/:\\/:g' 2.txt)/$(sed 's:/:\\/:g' 3.txt)/" > 4.txt
file B
& 에 한 줄만 있으면 유효하고 file C
, 두 줄 이상 있으면 유효하지 않습니다.
답변1
가장 간단한 방법 sed
은 이 두 목록을 처리하여 하나로 바꾸는 것입니다.스크립트 파일예를 들어
s/line1-from-fileB/line1-from-fileC/g
s/line2-from-fileB/line2-from-fileC/g
....................................
s/lineN-from-fileB/lineN-from-fileC/g
그런 다음 sed
실행하고 fileA
편집합니다.적절한접근 방식은 먼저 LHS
/를 처리 RHS
하고 해당 줄에 나타날 수 있는 특수 문자를 이스케이프한 다음 , 구분 기호 및 (예: with )를 결합 LHS
및 RHS
추가 하고 결과를 다음으로 파이프하는 것입니다 .s
/
g
paste
sed
paste -ds///g /dev/null /dev/null \
<(sed 's|[[\.*^$/]|\\&|g' fileB) <(sed 's|[\&/]|\\&|g' fileC) \
/dev/null /dev/null | sed -f - fileA
따라서 one paste
과 three는 sed
줄 수에 관계없이 각 파일을 한 번만 처리합니다.
이는 쉘이 프로세스 대체를 지원하고 다음을 sed
읽을 수 있다고 가정합니다.스크립트 파일~에서표준 입력. 또한, 그 자리에서 편집이 되지 않습니다. ( -i
모든 sed
버전이 지원하는 것은 아니기 때문에 스위치는 생략했습니다 .)
답변2
대체를 서로 독립적으로 수행하려면 다음과 같이 하십시오.
foo -> bar
bar -> foo
에 적용
foobar
결과적으로:
barfoo
foofoo
순진한 번역 과는 달리 s/foo/bar/g; s/bar/foo/g
다음과 같이 할 수 있습니다.
perl -pe '
BEGIN{
open STRINGS, "<", shift@ARGV or die"STRINGS: $!";
open REPLACEMENTS, "<", shift@ARGV or die "REPLACEMENTS: $!";
while (defined($a=<STRINGS>) and defined($b=<REPLACEMENTS>)) {
chomp ($a, $b);
push @repl, $b;
push @re, "$a(?{\$repl=\$repl[" . $i++. "]})"
}
eval q($re = qr{) . join("|", @re) . "}";
}
s/$re/$repl/g' strings.txt replacements.txt fileA
이는 perl
에서 예상되는 정규식 입니다 patterns.txt
. Perl 정규식은 임의의 코드를 실행할 수 있으므로 이를 삭제하는 것이 중요합니다. 고정 문자열만 바꾸려면 다음과 같이 변경할 수 있습니다.
perl -pe '
BEGIN{
open PATTERNS, "<", shift@ARGV or die"PATTERNS: $!";
open REPLACEMENTS, "<", shift@ARGV or die "REPLACEMENTS: $!";
for ($i = 0; defined($a=<PATTERNS>) and defined($b=<REPLACEMENTS>); $i++) {
chomp ($a, $b);
push @string, $a;
push @repl, $b;
push @re, "\\Q\$string[$i]\\E(?{\$repl=\$repl[$i]})"
}
eval q($re = qr{) . join("|", @re) . "}";
}
s/$re/$repl/g' patterns.txt replacements.txt fileA
답변3
각 대상 단어가 파일에서 한 번만 발생한다는 것을 보여주는 이 간단한 예에서는 간단히 다음을 수행할 수 있습니다.
$ paste fileB fileC | while read a b; do sed -i "s/$a/$b/" fileA; done
$ cat fileA
Hello John, how is your wife? where is grandpa?
이 paste
명령은 결합된 두 파일의 데이터를 인쇄합니다.
$ paste fileB fileC
Peter John
dad wife
mom grandpa
while read
각 행을 반복하는 간단한 루프를 통해 fileB
as $a
및 fileC
as 의 값을 저장합니다 $b
. 그런 다음 명령은 sed
첫 번째 항목을 . 이것을 세 번 반복하십시오.$a
$b
대상 단어가 파일에 한 번만 나타난다는 것을 알고 있고(그렇지 않으면 대체해야 하는 단어를 결정하는 데 사용할 수 있는 추가 세부 정보를 제공해야 함) 파일이 작은 경우 이 접근 방식은 좋습니다. , 당신이 시연했듯이. 더 큰 파일의 경우 각 단어 쌍에 대해 한 번 실행해야 하므로 시간이 오래 걸리고 매우 비효율적입니다.
따라서 더 큰 파일이 있는 경우 다음과 같은 것을 원할 수 있습니다.
paste fileB fileC |
perl -lane '$words{$F[0]}=$F[1]}
END{open(A,"fileA"); while(<A>){s/$_/$words{$_}/ for keys %words; print}'
답변4
제가 만든 솔루션은 그다지 짧지는 않지만 충분히 간단하고 읽기 쉽습니다. 당신의 임무가 sed로 모든 일을 하는 것이 아니라면...?
#!/usr/bin/bash
cp A.txt D.txt
x=1
length=$(wc -l B.txt | sed 's/\ .*//g')
until [ $x -eq $length ]; do
Bx=$(awk "NR==$x" B.txt)
Cx=$(awk "NR==$x" C.txt)
sed -i "s/$Bx/$Cx/g" D.txt
x=$(($x+1))
done
rm -f ./sed*
B.txt가 C.txt보다 길거나 그 반대의 경우 이 스크립트는 많은 정크 파일을 생성한다는 점에 유의하세요(아직 테스트하지는 않았습니다).