sedを使用して、2つの文字列のすべての出現をきれいに交換します


13

StringAとStringBの両方が複数回出現するファイルがあるとします。StringAのすべての出現をStringBに置き換え、(同時に)StringBのすべての出現をStringAに置き換えます。

今、私は次のようなことをしています

cat file.txt | sed 's/StringB/StringC/g' | sed 's/StringA/StringB/g' | sed 's/StringC/StringA/g'

このアプローチの問題は、StringCがファイル内で発生しないことを前提としていることです。これは実際問題ではありませんが、この解決策はいまだに汚い感じがします。つまり、Unixの魔法をさらに学ぶ機会のように感じます。:)

回答:


11

同じ入力行に表示できない場合はStringBStringAsedに一方向の置換を実行し、最初に検索された文字列が発生しなかった場合にのみ他の方法を試すように指示できます。

<file.txt sed -e 's/StringA/StringB/g' -e t -e 's/StringB/StringA/g'

一般的な場合、sedには簡単な方法はないと思います。ちなみに、ノートでは仕様があれば曖昧であることStringAStringB重複することができます。これは、どちらかの文字列の左端の出現を置き換えて繰り返すPerlソリューションです。

<file.txt perl -pe 'BEGIN {%r = ("StringA" => "StringB", "StringB" => "StringA")}
                    s/(StringA|StringB)/$r{$1}/ge'

POSIXツールを使い続けたい場合は、awkが最適です。Awkには一般的なパラメータ化された置換のためのプリミティブがないため、独自にロールする必要があります。

<file.txt awk '{
    while (match($0, /StringA|StringB/)) {
        printf "%s", substr($0, 1, RSTART-1);
        $0 = substr($0, RSTART);
        printf "%s", /^StringA/ ? "StringB" : "StringA";
        $0 = substr($0, 1+RLENGTH)
    }
    print
}'

最初のコマンドを実行すると、sedから通知されますsed: can't read s/StringB/StringA/g: No such file or directory。そうです-e t PATTERN、よく理解されていません。
Gyscos

1
@Gyscos -e2番目のsコマンドの前に行方不明がありました。答えを修正しました。
ジル 'SO-悪であるのをやめる'

8

現在、私は
...............
のようなことをしています。このアプローチの問題は、ファイル内でStringCが発生しないと仮定していることです。

あなたのアプローチは素晴らしいと思う、あなたは文字列の代わりに何か他のものを使うべきだ、何かは行(パターンスペース)で発生することはできません。最適な候補は、\newlineです。
通常、パターンスペースの入力行にはその文字が含まれないため、ファイル内THISおよびTHATファイル内のすべての出現箇所を交換するには、次を実行できます。

sed 's/THIS/\
/g
s/THAT/THIS/g
s/\n/THAT/g' infile

または、sed \nがRHSでもサポートしている場合:

sed 's/THIS/\n/g;s/THAT/THIS/g;s/\n/THAT/g' infile

1
これは美しいです。少し泣いた。RHS改行を実行する別の方法はシェル変数です- sedいくつかのマクロを事前に準備する場合、特定のエスケープをサポートするかどうかはそれほど重要ではなくなります。のようにset /THIS /THAT "$(printf \\n/)"; sed "s/$2/\\$4g;s/$3$2/g;s/\\n$3/g"-確かにここではちょっと愚かですが、特にcharクラスなどの場合は、もっと意味があります。
mikeserv

まあ、それはどうですか、男。それについての答えさえあります。私がコメントをしたとき、それはありましたか?最近編集したリストに(おそらく)ポップアップが表示され、一番上の答えの一番上の行が少しずれていました(非組み込みLinuxだけを気にしているなら、私は推測します)。そこで私はGillesの提案を好みます-長時間実行しているのでなければsed、一定のフォークのオーバーヘッドeはkinduvaの悪夢です。別のメモ-私はpaste丸一日遊んでいます。オプションパーサーを作成しましたcolumn。入力文字列と文字列を一緒にダッシュするだけのダッシュです。
mikeserv

3

「nonce」文字列を使用して2つの単語を入れ替えることは完全に有効だと思います。より一般的なソリューションが必要な場合は、次のようなことができます。

sed 's/_/__/g; s/you/x_x/g; s/me/you/g; s/x_x/me/g; s/__/_/g' <<<"say you say me"

それはもたらす

say me say you

x_x「x_x」文字列がある場合に置換を避けるために、ここで2つの追加の置換が必要であることに注意してください。しかし、それでもawk私にとっては解決策よりも簡単に思えます。


それはAskerが彼らがすでにやっていたことを言ったことのようです。
ロアイマ

1
はい、最初は見落としていましたが(編集履歴を参照)、元の文字列で置換文字列(ここでは "x_x")が発生する場合でも動作するため、私の解決方法は異なります。
デビッドオンガロ

スマートですが、キャッチがあります。StringAまたはStringBにが含まれる場合__それ自体を調整する(別の文字を選択する)か、問題のある文字列(s/_/__/g事前に実行することをお勧めします)を調整する必要があります。あなたのソリューションは、そのままでは、盲目的に適用して任意の文字列を交換することはできません。
カミルマシオロウスキ

@KamilMaciorowski私はあなたが何を意味するのか理解できませんか?実際にs/_/__/g事前に申し込みます。たぶん、失敗したテストケースを表示するだけかもしれません。
デビッドオン

@KamilMaciorowskiああ、私は今理解したと思います。あなたは、置換文字列そのものが含まれている場合を意味する_ので、交換すると言うy_oume。はい、それを認識y__ouし、表現に入れなければならないのは本当です。入力パラメータとして置換を使用するスクリプトでも、それを考慮する必要があります。
デビッドオン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.