Dosyadaki bir desenin yalnızca N'inci oluşumunu nasıl değiştirebilirim?


10

sedKomut kullanarak dosyadaki dizenin üçüncü oluşumunu değiştirme .

Misal:

Sadece üçüncü oluşumunu değiştirme isiçin usdosyada.

Giriş dosyam şunları içerir:

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

Çıktı bekliyorum:

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

3
Giriş ve çıkış aynı.
Hauke ​​Laging

4
sediş için doğru araç değildir.
choroba

@don_crissti Çözdüm. OP, biçimlendirme araçlarını kullanmamıştı (bu arada, Sureshkumar, sorularınızı düzenleme konusunda yardım için buraya bakın ) ve ardışık editörler ne istediğini yanlış anlamıştı.
terdon

Yanıtlar:


11

Bunu yapmak çok daha kolay perl.

3. olayı değiştirmek için :

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

Her 3 değiştirmek için rd oluşumunu:

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

3

Değiştirme dizesi her satırda yalnızca bir kez oluştuğunda, farklı yardımcı programları birleştirebilirsiniz.
Girdi "input" dosyasındayken ve "is" by "us" ile değiştirdiğinizde,

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

Sorudaki örnekte, issatır başına birden fazla var .
terdon

Boşluklu "is" aradığınızı sanıyordum. Cevabımı @jimmij gibi tr komutuyla düzenleyebilirdim, ama benim çözümüm onun için çok daha aşağı olurdu.
Walter A

Ben asker değilim :). Sana OP beklenen göreceksiniz Cevabını upvoted etmişti yüzden aynı şeyi düşündüm, ancak sorunun orijinal sürümüne bakarsak (lütfen "önce Düzenleyen X dakika" üzerine tıklayın) olduğu içinde bu değiştirilecek ve böylece . Bu arada, orada kediye ihtiyaç yok.
terdon

2

Aşağıdaki komut dosyası ( GNU sed sözdizimi kullanılarak), istenen değişiklikten sonra yazdırma satırlarını durdurduğundan çıktı için değil yerinde düzenleme için kullanılabilir:

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

Beğendiğiniz choroba kararınızı yukarıda değiştirebilirsiniz.

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

tüm hatların çıktısını veren

Ya da tüm satırları desen alanına koymanız gerekir (hafızaya boyut sınırlamasına dikkat edin) ve değiştirme yapın

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

2

sedÖnceden yeni satırlar başka karakterlerle değiştirilirse bunun için kullanabilirsiniz , örneğin:

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

Ve aynı şey saf (GNU) için sed:

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

( https://stackoverflow.com/a/1252191/4488514 adresindensed yeni satır değişikliği utanmadan çalındı )


GNU'ya sedözel sözdizimi kullanacaksanız , bunu da kullanabilirsiniz sed -z 's/is/us/3'.
Stéphane Chazelas

@ StéphaneChazelas -zyepyeni bir özellik olmalı, GNU sed version 4.2.1bu seçenek hakkında hiçbir şey bilmiyorum.
jimmij

1
4.2.2 (2012) 'de eklendi. İkinci çözümünüzde, \x0adım adım dönüştürmeye ihtiyacınız yoktur .
Stéphane Chazelas

Düzenleme için üzgünüm. Sorunun orijinal versiyonunu görmemiştim ve birileri soruyu yanlış anlamış ve yanlış satırı düzenlemişti. Önceki sürüme geri döndüm.
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'

Bu parça sedsadece isbir satırdan diğerine olayların bir çetelesini taşır . isSatır başına attığınız kadar çok es'yi güvenilir bir şekilde ele almalıdır ve eski satırları arabelleklemesine gerek yoktur - iskarşılaştığı her şey için başka bir kelimenin parçası olmayan tek bir yeni satır karakteri tutar .

Sonuç olarak, bir dosyada yalnızca üçüncü olayı değiştirecek ve satır başına sayı taşıyacaktır. Yani bir dosya şöyle görünürse:

1. is is isis
2. is does

... yazdıracak ...

1. is is isis
2. us does

Her satırın başına ve kuyruğuna boşluk bırakarak ilk önce kenar kasaları ele alır. Bu, kelime sınırlarının anlaşılmasını biraz daha kolaylaştırır.

Daha sonra , sıfırdan veya bir noktalama karakterinden hemen önce bir boşluk izleyen isbir \newline ekleyerek geçerli es arar is. Başka bir geçiş yapar ve \nhemen önünde boşluk olmayan bir karakter olan tüm ewline'ları kaldırır . Geride kalan bu işaretçiler eşleşir is., isancak eşleşmez thisveya ?is.

Daha sonra her bir işaretçiyi dizenin kuyruğuna toplar - \nibir çizgideki her eşleşme \niçin dizenin kuyruğuna bir ewline ekler ve onunla iveya ile değiştirilir u. Eğer \ndizginin kuyruğunda toplanmış bir satırda 3 ewline varsa u - else i'yi kullanır. Au ilk kullanıldığında da sonuncusudur - değiştirme, aşağı doğru kaybolan sonsuz bir döngü başlatır get line, print line, get line, print line,.

Her deneme döngüsü döngüsünün sonunda, eklenen boşlukları temizler, desen alanında yalnızca ilk oluşan satır sonuna kadar yazdırır ve tekrar gider.

lDöngünün başına bir ook komutu ekleyeceğim :

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

... ve bu girdiyle çalışırken neler yaptığına bir göz atın:

hai this is linux.
hai this is unix.


hai this is mac.
hai this is unchanged is.

... işte burada yaptığı şey:

 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

isHer satırda daha fazla es ile daha mantıklı :

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'
)        

Bu hemen hemen aynı şey ama POSIX BRE ve ilkel argüman yönetimiyle yazılmış.

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

... alır ...

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

... ve etkinleştirirsem ${dbg}:

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

... yinelemesini izleyebiliriz ...

 \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

Örneğinizin "isis" dediğini anladınız mı?
flarn2006

@ flarn2006 - im olduğunu söyledi eminim.
mikeserv

0

İşte çalışabilmesi için bir betikte kullanılan sedve tryazılması gereken mantıksal bir çözüm . Aşağıdaki kod , komutta belirtilen sözcüğün her 3. tekrarının yerine geçer sed. Bunun herhangi biri için çalışmasını sağlamak için i=3ile değiştirin .i=nn

Kod:

# 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


Bu neden işe yarıyor:

Metin dosyasının olduğunu varsayalım 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 olduğunda: öğesinin her ikinci tekrarını değiştirmek istiyoruz b.

    • a b b b b a c a d a b b b a b e b z b s b a b
      . . ^ . ^ . . . . . . ^ . . ^ . . . ^ . ^ . ^
    • Önce 2. tekrar, sonra 3. tekrar, sonra 4., 5. vb. Bunu kendiniz görmek için yukarıda gösterilen sırayı sayın.
  • N = 3 olduğunda: öğesinin her üçüncü tekrarını değiştirmek istiyoruz b.

    • a b b b b a c a d a b b b a b e b z b s b a b
      . . . ^ . . . . . . . ^ . . . . ^ . . . . . ^
    • İlk olarak 3. olayı, ardından 5., sonra 7., 9., 11., vb.
  • N = 4 olduğunda: öğesinin her üçüncü tekrarını değiştirmek istiyoruz b.

    • İlk olarak 4. olayı, daha sonra 7'nci, daha sonra 10'uncu, 13'üncü vb.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.