Bir karakteri tam olarak bir kez içeriyorsa satır nasıl kaldırılır


10

Bir kereden fazla mevcutsa veya mevcut değilse, dosyayı dosyada tuttuğunuzda belirli bir karakteri içeren bir dosyadan bir satırı kaldırmak istiyorum.

Örneğin:

DTHGTY
FGTHDC
HYTRHD
HTCCYD
JUTDYC

Burada, kaldırmak istediğim karakter Cböyledir, komut satırları FGTHDCve tam olarak bir kez JUTDYCsahip oldukları için kaldırmalıdır C.

Nasıl birini kullanarak bunu yapabilirsiniz sedveya awk?

Yanıtlar:


20

In awksize bir şey alan ayırıcı ayarlayabilirsiniz. Bunu olarak ayarlarsanız C, o zamanlar kadar +1 alanınız olur C.

Demek ki eğer awk -F'C' '{print NF}' <<< "C1C2C3"sen almak 4: CCC3 oluşur Cs ve dolayısıyla 4 alanlar.

CTam olarak bir kez oluşan satırları kaldırmak istiyorsunuz . Bunu göz önünde bulundurarak, sizin durumunuzda tam olarak iki Calanın bulunduğu satırları kaldırmak isteyeceksiniz. Bu yüzden onları atlayın:

$ awk -F'C' 'NF!=2' file
DTHGTY
HYTRHD
HTCCYD

4
awkAlan ayırıcının zekice kullanımı !
Valentin B.

interresting, varsayılan durumda olduğu gibi (FS = "") önde gelen boşlukları ($ 1 = satırdaki ilk boşluk olmayan) ve tekrarları (alan 1 ve alan 2'yi ayırmak için 5 alanınız olabilir) yok sayar ... boşluk Muhtemelen özel muamele görüyor mu? (görmek için, awk 'BEGIN { print "FS={" FS"}","OFS={" OFS "}";} {printf "%d fields : ",NF; for (i=1;i<=NF;i++) {printf "{" $i "} ";}; print "" }'bazıları birden fazla spesi olan ve bazıları alan (lar) ile başlayan bazı çizgiler yapabilir ve besleyebilir)
Olivier Dulac

2
@OlivierDulac, evet, boşluk POSIX tarafından belirtildiği gibi özel olarak ele alınır .
Joker

8

sed yaklaşım:

sed -i '/^[^C]*C[^C]*$/d' input

-i seçeneği, yerinde dosya değiştirmeye izin verir

/^[^C]*C[^C]*$/- Cyalnızca bir kez içeren satırlarla eşleşir

d - eşleşen satırları sil


8

Bu şu şekilde yapılabilir sed:

Kod:

sed '/C.*C/p;/C/d' file1

Sonuçlar:

DTHGTY
HYTRHD
HTCCYD

Nasıl?

  1. Eşleşme ve en az iki kopya ile herhangi bir çizgiyi basmak Cyoluyla/C.*C/p
  2. Bir Cyolla herhangi bir satırı silin /C/d, bu adım 1'de zaten yazdırılmış olan satırları içerir
  3. Varsayılan satırların geri kalanını yazdırma

2
Akıllı alternatif yaklaşım; Bunu sevdim.
Joker

6

Bu, tam olarak bir C oluşumuyla çizgileri kaldırır.

grep -v '^[^C]*C[^C]*$' file

Normal ifade [^C], C (veya yeni satır) olmayan bir karakterle eşleşir ve yineleme operatörü (Kleene yıldızı olarak da bilinir) *, önceki ifadenin sıfır veya daha fazla tekrarını belirtir.

Varsayılan çıktı grep(ve diğer metin odaklı araçların çoğu) standart çıktıdır; yeni bir dosyaya yönlendirin ve isterseniz bu dosyayı orijinal dosyanın üstüne taşıyın. Aynı normal ifade, sed -iyerinde düzenleme için de kullanılabilir :

sed -i '/^[^C]*C[^C]*$/d' file

(Bazı platformlarda, özellikle * macOS dahil BSD, -iseçenek gibi bir argüman gerektirir -i ''.)


1
sed -i '/^[^C]*C[^C]*$/d' file- daha önce gönderilmiş gibi geliyor, sizce intihal?
RomanPerekhrest

1
Gerçekten de, bazı tekrarlar var. Cevapla başladım grepama açıkçası kolayca sed -ivaryanta uzanıyor . Cevabınızı görmedim çünkü önceki grepcevapları arıyordum .
üçlü 3

1
Sadece açıkça önlemek için daha güvenli -iolan sedve bunun yerine yeni bir dosyaya yönlendirmek ve eğer bununla orijinali yerine sedyarar hiçbir hata ile sonlandı.
Kusalananda

2
Veyagrep -vx '[^C]*C[^C]*'
Stéphane Chazelas

@Kusalananda Ancak daha grepaçık ve daha sağlam olduğu için de kullanabilirsiniz (özellikle seddaha az bilgilendirici bir çıkış kodu vardır).
üçlü 3

4

Bir dosyanın komut dosyası düzenlemeleri için POSIX aracı (değiştirilen içeriği standart çıktıya yazdırmak yerine) ex.

printf '%s\n' 'g/^[^C]*C[^C]*$/d' x | ex file.txt

Tabii ki olabilir kullanmaksed -i Sed sürümünüz destekliyorsa, sadece en sistemlerinin farklı türlerinde çalıştırmak amacıyla bir senaryo yazıyorsanız taşınabilir değil unutmayın.


David Foerster yorumlarda sordu:

Bir kullandığınız sebebi var mıdır printfdeğil echogibi falan ex -c COMMAND?

Cevap: Evet.

İçin printfvs. echoo taşınabilirlik meselesi; bkz. printf neden yankıdan daha iyidir? Ayrıca komutları kullanarak yeni satırlar arasında geçiş yapmak daha kolaydır printf.

İçin printf ... | exvs ex -c ..., bu hata işleme meselesi. Bu özel komut için önemli değil, ama genel olarak önemli; örneğin,

ex -c '%s/this pattern is not in the file/replacement text/g | x' filename

bir senaryoda. Aşağıdakilerle karşılaştırın:

printf '%s\n' '%s/no matching lines/replacement/g' x | ex file

İlki asılır ve girişi bekler; ikincisi EOF exkomut tarafından alındığında çıkacaktır, böylece kod devam edecektir. Gibi alternatif çözümler vardır s///e, ancak POSIX tarafından belirtilmezler. Yukarıda gösterilen portatif formu kullanmayı tercih ederim.

İçin gkomuta, orada olmalıdır sonunda bir satır olacak ve ben kullanmayı tercih printfkomutları sarmak için yerine tek tırnak içinde bir yeni satır gömme.


1
Bir kullandığınız sebebi var mıdır printfdeğil echogibi falan ex -c COMMAND?
David Foerster

@DavidFoerster, evet. Size yorumlarda cevap vermeye başladım ama uzun büyüdü, bu yüzden cevaba ekledim.
Joker

Teşekkürler ve +1! Ben biliyordum printfvs. echo(Ben genellikle sadece tercih olsa echoargümanı sert kodlu olduğunda) ama kullanmadıysanız exşimdiye kadar yoğun.
David Foerster

2

İşte perl kullanarak birkaç seçenek.

Yalnızca tek bir karakterle tr/C//eşleştiğiniz için, Caşağıdakilerin eşleşme sayısını döndürmek için (yerine koymadan bir çeviri) kullanabilirsiniz :

perl -lne 'print if tr/C// != 1' file

Daha genel olarak, çok karakterli bir dizeyi veya normal ifadeyi eşleştirmek istiyorsanız, bunu kullanabilirsiniz:

perl -lne 'print if (@m = /C/g) != 1' file

Bu, normal ifadenin eşleşmelerini /C/gbir listeye atar ve @mbu listenin uzunluğu olmadığında satırlar yazdırır 1.

-iAnahtarı "yerinde" düzenleme eklenebilir.


2
sed -e '
  s/C/&/2;t   # when 2nd C matches skip processing and print
  /C/d        # either one C or no C, so delete on C
'

sed -e '
   /C/!b     # no C, skip processing and print
   /C.*C/!d  # not(at least 2 C) => 1 C => delete
'

perl -lne 's/C/C/g == 1 or print'

GNU olduğunu varsayar sed, t #...genellikle #...diğer seduygulamaların çoğunda adı verilen etikete dallanır .
Stéphane Chazelas

Hatta !bGNU sed bile, şube bir etiket veya bir satırsonu dışında hiçbir şeyi sevmiyor.

Evet, b, t, :, }(ve r file, w file...) aynı satırda onlardan sonra bir komut olamaz. Ayrı -eseçenekler de kullanabilirsiniz .
Stéphane Chazelas

Perl seçeneğiniz doğru çıktıyı üretmiyor. Sanırım gdeğiştiriciyi eklemeyi unuttun .
Tom Fenech

@TomFenech Doğru. Bunu düzeltiyorum. Teşekkürler.

1

awkÖzellikle isteyen herkes için

awk '/C[^C]*C/{next}//{print}'

desenle eşleşiyorsa satırı atlayın, aksi takdirde yazdırın. Aslında gerek yok {print}, kullanabilirsiniz //ve varsayılan baskı, ama bence daha net yazılmış.

İlk düşüncem egrep -vaynı kalıpla kullanmaktı , ama aslında soruyu olduğu gibi cevaplamıyor.


1
Bundan sonra herhangi bir şeyi eşleştirmenin anlamı nedir {next}? Sadece söyle awk '/pattern/ {next} 1've desene uymayan tüm çizgiler yazdırılacak. Ya da daha iyisi, awk '!/pattern/'bunları doğrudan yazdırmak için.
fedorqui

hakkında @fedorqui iyi bir noktaya !/pattern/(her nasılsa unutmuşum) ama çok yerine bir kendini açıklayıcı görürdünüz //{print}şifreli daha 1. Kodunuzu korumak için bir sonraki kişiden en az yetkinliği ve akıcılığı kabul edin, kodunuzu daha az verimli veya etkili hale getirmemekle tutarlı olarak.
nigel222
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.