次の文字列を置き換えようとしています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
& に 1 行だけあれば有効でfile C
、 2 行以上あれば有効ではありません。
答え1
最も簡単な方法sed
は、これら2つのリストを処理して1つに置き換えることです。スクリプトファイル例えば
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
したがって、onepaste
と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
コマンドは、結合された2つのファイルのデータを印刷します。
$ paste fileB fileC
Peter John
dad wife
mom grandpa
while read
各行を繰り返す単純なループを介してfileB
as$a
とfileC
asの値を保存します$b
。次に、コマンドはsed
最初の項目を 。これを3回繰り返します。$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より長い場合、またはその逆の場合、このスクリプトは多くのジャンクファイルを生成することに注意してください(まだテストしていません)。