ファイル内のパターンのN番目の出現のみを置き換える方法は?


10

sedコマンドを使用してファイル内の3番目の文字列を置き換える方法。

例:

唯一の第三の発生を変更isするusファイルに。

私の入力ファイルには以下が含まれます:

hai this is linux.
hai this is unix.
hai this is mac.
hai this is unchanged.

私は出力が期待されています:

hai this is linux.
hai thus is unix.
hai this is mac.
hai this is unchanged.

3
入力と出力は同じです。
Hauke Laging、2015年

4
sed仕事に適したツールではありません。
チョロバ2015年

@don_crissti直した。OPはフォーマットツールを使用していませんでした(ちなみに、Sureshkumar、質問の編集に関するヘルプはこちらを参照てください)。その後の編集者は、必要なものを誤解していました。
terdon

回答:


11

を使用すると、はるかに簡単perlです。

3 番目のオカレンスを変更するには:

perl -pe 's{is}{++$n == 3 ? "us" : $&}ge'

3 番目ごとに変更するには:

perl -pe 's{is}{++$n % 3 ? $& : "us"}ge'

3

置換文字列が1行に1回だけ出現する場合、さまざまなユーティリティを組み合わせることができます。
入力がファイル「input」にあり、「is」を「us」に置き換えている場合、使用できます

LINENR=$(cat input | grep -n " is " | head -3 | tail -1 | cut -d: -f1)
cat input | sed ${LINENR}' s/ is / us /'

質問の例では、is1行に複数あります。
terdon

探しているのは、スペースがある「is」だと思いました。@jimmijのようにtrコマンドを使用して回答を編集することはできましたが、私の解決策は彼よりはるかに劣ります。
Walter A

私は質問者ではありません:)。私はあなたの答えをupvotedていた理由である、同じことを思っていますが、(リンク「前編集X分」をクリックしてください)質問のオリジナルバージョンを見れば、あなたはOPが期待されることがわかりますです、このこのように変更されます。ちなみにはいません
terdon

2

以下のスクリプト(GNU sed構文を使用)は、目的の置換後に印刷行を停止するため、出力ではなくインプレース編集に使用できます。

sed -i '/is/{: 1 ; /\(.*is\)\{3\}/!{N;b1} ; s/is/us/3 ; q}' text.file

あなたのようなチョロバの決定であれば、上記のように変更できます

sed '/is/{:1 ; /\(.*is\)\{3\}/!{N;b1} ; s/is/us/3 ; :2 ; n ; $!b2}' text.file

すべてのラインを出力します

または、すべての行をパターン空間(メモリ内にあるのでサイズ制限に注意してください)に配置して置換を行う必要があります

sed ': 1 ; N ; $!b1 ; s/is/us/3 ' text.file

2

sed以前に改行を他の文字に置き換えた場合は、次のように使用できます。

tr '\n' '\000' | sed 's/is/us/3' | tr '\000' '\n'

そして純粋な(GNU)と同じsed

sed ':a;N;$!ba;s/\n/\x0/g;s/is/us/3;s/\x0/\n/g'

https://stackoverflow.com/a/1252191/4488514sedから恥知らずに盗まれた改行置換)


GNU sed固有の構文を使用する場合は、を使用することもできますsed -z 's/is/us/3'
ステファンChazelas

@StéphaneChazelas -zはまったく新しい機能である必要がありGNU sed version 4.2.1ます。このオプションについては何も知りません。
jimmij 2015年

1
4.2.2(2012)で追加されました。2番目のソリューションでは、\x0ステップへの変換は必要ありません。
ステファンChazelas

編集について申し訳ありません。私は質問の元のバージョンを見ていなかったので、誰かがそれを誤解し、間違った行を編集しました。以前のバージョンに戻しました。
terdon

1
p='[:punct:]' s='[:space:]'
sed -Ee'1!{/\n/!b' -e\}            \
     -e's/(\n*)(.*)/ \2 \1/'       \
     -e"s/is[$p]?[$s]/\n&/g"       \
     -e"s/([^$s])\n/\1/g;1G"       \
-e:c -e"s/\ni(.* )\n{3}/u\1/"      \
     -e"/\n$/!s/\n//g;/\ni/G"      \
     -e's//i/;//tc'                \
     -e's/^ (.*) /\1/;P;$d;N;D'

そのビットは、ある行から次の行への発生のsed集計を運ぶだけisです。それはisあなたがそれに投げるのと同じくらい多くのesを行ごとに確実に処理するべきであり、それがそうしている間古い行をバッファリングする必要はありません-それisは別の単語の一部ではない遭遇するすべての単一の改行文字を保持するだけです。

その結果、ファイル内の3番目のオカレンスのみが変更され、1行あたりのカウントが含まれます。したがって、ファイルが次のようになっているとします。

1. is is isis
2. is does

...印刷されます...

1. is is isis
2. us does

まず、すべての行の先頭と末尾にスペースを挿入して、エッジケースを処理します。これにより、単語の境界が少しわかりやすくなります。

次にis\nすべての出現箇所isが0個または1個の句読文字の直後にスペースが続く前に、ewlineを挿入して有効なesを探します。別のパスを\n実行し、スペース以外の文字が直前にあるすべてのewlineを削除します。残されたこのマーカーが一致するis.isはありませんthis?is

次に、各マーカーを文字列の末尾に集めます。\ni行が一致するたび\nに、文字列の末尾にewlineが追加され、iまたはに置き換えられますu\n文字列の末尾に集められた3つのewlinesが連続している場合は、uを使用します。それ以外の場合はiを使用します。auが最初に使用されたときも最後です-置換により、無限ループが発生get line, print line, get line, print line,します。

各tryループサイクルの最後に、挿入されたスペースをクリーンアップし、パターンスペースで最初に出現する改行までのみを印刷してから、再度実行します。

次のlように、ループの先頭にookコマンドを追加します。

l; s/\ni(.* )\n{9}/u\1/...

...そして、この入力で機能するときに何が行われるかを確認します。

hai this is linux.
hai this is unix.


hai this is mac.
hai this is unchanged is.

...だからここにそれが何をするかです:

 hai this \nis linux. \n$        #behind the scenes
hai this is linux.               #actually printed
 hai this \nis unix. \n\n$       #it builds the marker string
hai this is unix.
  \n\n\n$                        #only for lines matching the

  \n\n\n$                        #pattern - and not otherwise.

 hai this \nis mac. \n\n\n$      #here's the match - 3 ises so far in file.
hai this us mac.                 #printed
hai this is unchanged is.        #no look here - this line is never evaled

is1行あたりのes が多ければ多いほど意味があります。

nthword()(  p='[:punct:]' s='[:space:]'         
    sed -e '1!{/\n/!b' -e\}             \
        -e 's/\(\n*\)\(.*\)/ \2 \1/'    \
        -e "s/$1[$p]\{0,1\}[$s]/\n&/g"  \
        -e "s/\([^$s]\)\n/\1/g;1G;:c"   \
        -e "${dbg+l;}s/\n$1\(.* \)\n\{$3\}/$2\1/" \
        -e '/\n$/!s/\n//g;/\n'"$1/G"    \
        -e "s//$1/;//tc" -e 's/^ \(.*\) /\1/'     \
        -e 'P;$d;N;D'
)        

これは実質的に同じことですが、POSIX BREと基本的な引数処理を使用して記述されています。

 printf 'is is. is? this is%.0s\n' {1..4}  | nthword is us 12

...取得...

is is. is? this is
is is. is? this is
is is. is? this us
is is. is? this is

...そして私が有効にした場合${dbg}

printf 'is is. is? this is%.0s\n' {1..4}  | 
dbg=1 nthword is us 12

...それが繰り返されるのを見ることができます...

 \nis \nis. \nis? this \nis \n$
 is \nis. \nis? this \nis \n\n$
 is is. \nis? this \nis \n\n\n$
 is is. is? this \nis \n\n\n\n$
is is. is? this is
 \nis \nis. \nis? this \nis \n\n\n\n\n$
 is \nis. \nis? this \nis \n\n\n\n\n\n$
 is is. \nis? this \nis \n\n\n\n\n\n\n$
 is is. is? this \nis \n\n\n\n\n\n\n\n$
is is. is? this is
 \nis \nis. \nis? this \nis \n\n\n\n\n\n\n\n\n$
 is \nis. \nis? this \nis \n\n\n\n\n\n\n\n\n\n$
 is is. \nis? this \nis \n\n\n\n\n\n\n\n\n\n\n$
 is is. is? this \nis \n\n\n\n\n\n\n\n\n\n\n\n$
is is. is? this us
is is. is? this is

あなたの例が「isis」と言っていることに気づきましたか?
flarn2006

@ flarn2006-それがそうであると私はかなり確信しています。
mikeserv 2016年

0

以下はsedtrそれを使用するためにスクリプトで記述する必要がある論理的なソリューションです。以下のコードは、コマンドで指定された単語の3番目の出現ごとに置き換えますsed。交換するi=3i=nいずれかのためにこの仕事をするためにn

コード:

# replace new lines with '^' character to get everything onto a single line
tr '\n' '^' < input.txt > output.txt

# count number of occurrences of the word to be replaced
num=`grep -o "apple" "output.txt" | wc -l`

# in successive iterations, replace the i + (n-1)th occurrence
n=3
i=3
while [ $i -le $num ]
do
    sed -i '' "s/apple/lemon/${i}" 'output.txt'
    i=$(( i + (n-1) ))
done

# replace the '^' back to new line character
tr '^' '\n' < output.txt > tmp && mv tmp output.txt


これが機能する理由:

テキストファイルがであるとしますa b b b b a c a d a b b b a b e b z b s b a b

  • n = 2の場合:の2番目ごとの出現を置き換えたいb

    • a b b b b a c a d a b b b a b e b z b s b a b
      . . ^ . ^ . . . . . . ^ . . ^ . . . ^ . ^ . ^
    • 最初に2番目のオカレンス、3番目のオカレンス、4番目、5番目のように置き換えます。上に示した順序で数えて、これを実際に見てください。
  • n = 3の場合:の3番目ごとの出現を置き換えbます。

    • a b b b b a c a d a b b b a b e b z b s b a b
      . . . ^ . . . . . . . ^ . . . . ^ . . . . . ^
    • 最初に3番目、次に5番目、7番目、9番目、11番目というように置き換えます。
  • n = 4の場合、の3番目ごとの出現を置き換えbます。

    • 最初に4番目、次に7番目、10番目、13番目というように置き換えます。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.