sed
コマンドを使用して、ファイル内で3番目に出現する文字列を置き換える方法。
例:
ファイル内で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.
Perl
を使用すると、はるかに簡単です。
3を変更するにはrd 発生:
Perl -pe 's{is}{++$n == 3 ? "us" : $&}ge'
3つごとに交換するrd 発生:
Perl -pe 's{is}{++$n % 3 ? $& : "us"}ge'
置換文字列が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 /'
以前に改行を他の文字に置き換えた場合は、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'
(sed
改行の置き換えは https://stackoverflow.com/a/1252191/4488514 から恥知らずに盗まれました)
以下のスクリプト([〜#〜] 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
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をラインごとに確実に処理するべきです、そしてそれはそれがしている間古いラインをバッファリングする必要はありません-それは別のWordの一部ではない、遭遇するすべてのis
に対して単一の改行文字を保持するだけです。
その結果、ファイル内の3番目のオカレンスのみが変更され、1行あたりのカウントが保持されます。したがって、ファイルが次のようになっているとします。
1. is is isis
2. is does
...印刷されます...
1. is is isis
2. us does
まず、各行の先頭と末尾にスペースを挿入してEdgeケースを処理します。これにより、Wordの境界を確認しやすくなります。
次に、ゼロまたは1つの句読文字の直後にスペースが続くis
のすべての出現箇所の前に\n
ewlineを挿入して、有効なis
esを探します。別のパスを実行し、スペース以外の文字が直前にあるすべての\n
ewlinesを削除します。残されたこのマーカーは、is.
およびis
と一致しますが、this
または?is
とは一致しません。
次に、各マーカーを文字列の末尾に収集します。行で\ni
が一致するたびに、文字列の末尾に\n
ewlineが追加され、i
またはu
に置き換えられます。文字列の末尾に収集された行に\n
ewlinesが3つある場合は、uを使用します。それ以外の場合はiを使用します。 uが最初に使用されたときも最後です-置換により、無限ループが始まり、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
1行あたりのis
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
sed
とtr
を使用する論理的なソリューションを次に示しますが、機能するためにはスクリプトで記述する必要があります。以下のコードは、sed
コマンドで指定されたWordのすべての番目の出現を置き換えます。置換i=3
とi=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の場合、b
の2番目のオカレンスをすべて置き換えます。
a b b b b a c a d a b b b a b e b z b s b a b
. . ^ . ^ . . . . . . ^ . . ^ . . . ^ . ^ . ^
N = 3の場合、b
の3番目ごとの出現を置き換えます。
a b b b b a c a d a b b b a b e b z b s b a b
. . . ^ . . . . . . . ^ . . . . ^ . . . . . ^
N = 4の場合、b
の3番目ごとの出現を置き換えます。