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
を使用してこれを行う最も簡単な方法は、これらの2つのリストを処理してscript-fileに変換することです。
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
が編集されます。 properの方法は、最初にLHS
/RHS
を処理し、それらの行に出現する可能性のある特殊文字をエスケープしてから、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
つまり、行数に関係なく、各ファイルを1回だけ処理するpaste
1つとsed
3つです。
これは、シェルがプロセス置換をサポートし、sed
がscript-fileをstdinから読み取ることができることを前提としています。また、インプレースで編集されません(-i
スイッチはすべてのsed
sでサポートされていないため、省略しました)。
交換を互いに独立して行う場合は、たとえば次のようにします。
foo -> bar
bar -> foo
に適用
foobar
結果として:
barfoo
素朴なs/foo/bar/g; s/bar/foo/g
のようなfoofoo
とは対照的に、次のようにすることができます。
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
patterns.txt
ではPerl
正規表現が必要です。 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
単純な例では、ターゲット単語のそれぞれがファイル内で1回だけ出現する場所を示しています。
$ 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
の値を$a
として保存し、fileC
の値を$b
として保存します。次に、sed
コマンドは、最初に出現する$a
を$b
に置き換えます。これを3回繰り返します。
ターゲットワードがファイルに1度だけ現れることがわかっている場合(そうでなければ、置換する必要があるオカレンスを識別するために使用できる詳細を提供する必要があります)、ファイルが小さい場合、このアプローチは問題ありません。 、あなたが示したように。大きなファイルの場合、これには長い時間がかかり、単語のペアごとに1回実行する必要があるため、非常に非効率的です。
したがって、大きなファイルがある場合は、代わりに次のようなものが必要になることがあります。
paste fileB fileC |
Perl -lane '$words{$F[0]}=$F[1]}
END{open(A,"fileA"); while(<A>){s/$_/$words{$_}/ for keys %words; print}'