Sınırlayıcı ile listeyi tek bir satıra dönüştür


17

Bu biçimde IP adreslerinin bir listesini (yükler) almak zorunda:

 134.27.128.0
 111.245.48.0
 109.21.244.0

ve aralarında bir boru (IP'ler oluşturulmuş) ile bu biçime dönüştürün

134.27.128.0 | 111.245.48.0 | 109.21.244.0 | 103.22.200.0/22

Bence bu bul ve değiştir komutu gibi sedama işe yarayamıyorum.


3
trYeni satırları |borulara eklemek mi istiyorsunuz ? Gibi <ipfile tr \\n \| >outfilemi?
mikeserv

Etraftaki boşluk |gerekli mi?
cuonglm

2
@uselesslinuxman - hayır. Giriş yönlendirmesine ihtiyacınız olacak <. Yani <mydoc tr \\n \| >mydoc2. Ama bu size boşluk bırakmayacak. Bunlar için, muhtemelen en hızlı çözümpaste -d' | ' mydoc /dev/null /dev/null >mydoc2
mikeserv

1
@mikeserv: İşe yarayacağını sanmıyorum. pasteher dosyaya karşılık gelen satırları yazar. Olmadan -s, dosyadaki satır sayısını geri alırsınız.
cuonglm

Yanıtlar:


16

Kullanılması dayalı sed : Ünlü Sed Tek Gömlekleri Bölüm I, Açıklaması : 39. ekleme bir ters eğik çizgi "\" ile biterse sonraki bir çizgi (biz ters eğik çizgi ilgili kısmı görmezden burada hariç, ve değiştirme \nile yeni satır gerekli |ayırıcı):

sed -e :a -e '$!N; s/\n/ | /; ta' mydoc > mydoc2

üretmeli mydoc2

134.27.128.0 |  111.245.48.0 |  109.21.244.0

@don_crissti üzgünüm bir tür oldu - düzeltildi, teşekkürler
steeldriver

Maalesef bu aslında pratikte işe yaramıyor. En azından sınırsız akış için değil. Bunu yaptığınızda, girişinizin tamamını bir satır yutmanız gerekir ve hepsini sindirinceye kadar tek bir bayt bile yazamazsınız - hepsi tek bir satıra dönüşür. Bu hantal ve segfault eğilimli.
mikeserv

Bir milyon IP, <16M'dir, burada sınırları aşmak için çok büyük bir listeye ihtiyacınız vardır. Eof tespiti için arama kullanmak daha sorunludur, çünkü giriş dosyası boyutunda O (N ^ 2) çalışacaktır. sed 'H;1h;$!d;x;s/\n/ | /g'doğrusaldır.
jthill

@jthill - POSIX yalnızca sed8K örüntü alanını garanti eder ; 16M'den çok daha az.
mikeserv

9

Ben merak ediyordum nasıl bunlardan bazıları (bazı alternatifler +) hız-bilge oldukça büyük bir dosya ile çalışma ( 163MiBbir IPsatıra ~ 13 milyon satır):

wc -l < iplist
13144256

Sonuçlar ( sync; echo 3 > /proc/sys/vm/drop_cachesher komuttan sonra; testleri - ters sırada - birkaç saat sonra tekrarladım, ancak farklar ihmal edilebilir; ayrıca kullandığımı da unutmayın gnu sed):

steeldriver :
Çok yavaş. İki dakika bekledikten sonra iptal edildi ... bu yüzden sonuç yok.

cuonglm :

awk 'FNR!=1{print l}{l=$0};END{ORS="";print l}' ORS=' | ' iplist

real    0m3.672s

perl -pe 's/\n/ | / unless eof' iplist

real    0m12.444s

mikeserv :

paste -d\  /dev/null iplist /dev/null | paste -sd\| - 

real    0m0.983s

jthill :

sed 'H;1h;$!d;x;s/\n/ | /g' iplist

real    0m4.903s

Avinash Raj :

time python2.7 -c'
import sys
with open(sys.argv[1]) as f:
    print " | ".join(line.strip() for line in f)' iplist

real    0m3.434s

ve

val0x00ff :

while read -r ip; do printf '%s | ' "$ip"; done < iplist

real    3m4.321s

anlamına gelir 184.321s. Şaşırtıcı olmayan bir şekilde, bu mikeserv'in çözümünden 200 kat daha yavaş .


İşte
awk ile başka yollar :

awk '$1=$1' RS= OFS=' | ' iplist

real    0m4.543s

awk '{printf "%s%s",sep,$0,sep=" | "} END {print ""}' iplist

real    0m5.511s

perl:

perl -ple '$\=eof()?"\n":" | "' iplist

real    0m9.646s

xargs:

xargs <iplist printf ' | %s' | cut -c4-

real    0m6.326s

kafa + macun + tr + cat kombinasyonu:

{ head -n -1 | paste -d' |' - /dev/null /dev/null | tr \\n \ ; cat ; } <iplist

real    0m0.991s

GNU coreutilsIP adresiniz varsa ve IP listeniz gerçekten büyük değilse (diyelim ki 50000 IP'ye kadar) bunu da yapabilirsiniz pr:

pr -$(wc -l infile) -tJS' | ' -W1000000 infile >outfile

nerede

-$(wc -l infile)         # no. of columns (= with no. of lines in your file)
-t                       # omit page headers and trailers
-J                       # merge lines
-S' | '                  # separate columns by STRING
-W1000000                # set page width

6 satırlık bir dosya için:

134.28.128.0
111.245.28.0
109.245.24.0
128.27.88.0
122.245.48.0
103.44.204.0

komuta:

pr -$(wc -l <infile) -tJS' | ' -W1000 infile

çıktılar:

134.28.128.0 | 111.245.28.0 | 109.245.24.0 | 128.27.88.0 | 122.245.48.0 | 103.44.204.0

don - Ayrıca while ... readdöngü için @ val0x00ff tarafından soruya öneri ekleyebilir misiniz ? Bir ölçütte 163k read()ve write()çağrıların ne anlama geldiğini merak ediyorum . Bu arada harika bir cevap.
mikeserv

1
@mikeserv - sorun değil, ben yapacağım ( gerçi gerçekten yavaş olacak ).
don_crissti

Bu gerçekten harika bir bağlantı. Özellikle yazarın da benzer bir 6 yıllık karşılaştırmalı teste bir bağlantı sunmasını seviyorum. Bunun sedo zamandaki konumunu geliştirdiğini (ve muhtemelen regexp motorunda çok az değişiklik olduğunu) fark ettiniz mi , ancak grepperformansında (özellikle daha uzun hatlar için) önemli ölçüde geride kalıyor gibi görünüyor mu? perlMotoruna yapılan eklemelerin bu sonuçlar üzerinde herhangi bir etkisi olup olmadığını merak ediyorum ... Bu da berbatdash olmayan bir şey . Burada muhtemelen çok yavaş w olurdu / ortak önüne. bashIFS=
mikeserv

hmm ... bu bağlantı, gerçekten C'yi öğrenip C'yi öğrenmem gereken başka bir güçlü göstergedir, böylece sonunda lexdüzgün bir şekilde kullanmaya başlayabilirim .
mikeserv

8

Awk kullanabilirsiniz :

awk 'FNR!=1{print l}{l=$0};END{ORS="";print l}' ORS=' | ' file > new_file

ORS=' | 'set çıkışı kayıt ayırıcısını için ' | 'yerine yeni satır.

veya şununla yerinde düzenleme yapın perl:

perl -pe 's/\n/ | / unless eof' file

teşekkürler dostum. Az önce nasıl pasteçalıştığını öğrendim . çok takdir etmek.
mikeserv

@mikeserv: Rica ederim. karşılaştırmasında gösterildiği gibi don_crissti, pasteçözüm en hızlı olanıdır.
cuonglm

Çıktı bir satırsonu ile bitmiyor. Bunu yapmak ORS=""için ENDbloğun içini değiştirmeniz gerekebilir ORS="\n".
phk

4

Bu yüzden her şeyi yanlış yaptım - ve bu soru bana çok şey öğretti paste. Cuonglm doğru bir şekilde belirttiği gibi, eğer seri pastebir dosyada -sdeğilseniz, her zaman dosya \nlistenizdeki son ewline'ı yazılı olarak çıktıya eklenir. paste -sDavranışın varsayılan modu olduğu inancında yanıldım - ve bu görünüşe göre busybox pastegüçlendirmekten mutluluk duyan bir yanlış anlama . Aşağıdaki komut, w / olarak tanıtıldığı gibi çalışır busybox:

paste -d'|  ' - - infile </dev/null >outfile

Yine de spec'e göre çalışmaz. Doğru bir şekilde uygulanan pasteyine de, \nyazılan her dizi için bir son ewline ekler . Yine de, bu hiç de önemli değil:

paste -d\  - infile - </dev/null | paste -sd\| - >outfile

@don_crissti - dangit. aptal tablet. Sanırım yapılacak en açık şey iki macun.
mikeserv

1
Aslında, aklımda vardı prama görünüşte bu yüzden büyük giriş dosyaları ile buhar biterse, bu yüzden aslında hızı test edemedim ama makul uzunluktaki dosyalar ile Tamam çalışıyor. Siz çözüm çok hızlı (sürpriz yok - pastegerçekten hızlı), yazıma bakın.
don_crissti

4

tr ve sed ile tek astar:

cat file | tr '\n' '|' | sed 's/||$/\n/'
134.27.128.0|111.245.48.0|109.21.244.0

Neden 2 sondaki boru silinir? Giriş boş bir satırla (iki yeni satır) sona erdiğinde yalnızca 2 olur.
JigglyNaga

3

Kullanın vim:

vim -n -u NONE -c '1,$-1s/\n/ | /g|wq!' data

Açıklama:

-n takas dosyasını devre dışı bırak

-u NONE tüm başlatmaları atlamak için kullanılır.

-c {command} dosya okunduktan sonra komutları yürütün.

1,$-1s/\n/ | /gbir s/\n/ | /garalık için (boşluk boru alanı ile yeni satır yerine) 1,$-1s(son satıra 1 satır 1 -)

wq! yazmaya ve çıkmaya zorla


Not:

Dosyanızın ne kadar büyük olduğuna bağlı olarak, bu kötü bir fikir olabilir.


1
Hepinize teşekkür ederim, çünkü temel olarak bu komutların neredeyse her biri başarmam gereken şey için çalışıyor. Tekrar sıkışıp kalsam şimdi nereye geleceğimi biliyorum. Teşekkürler
uselesslinuxman

2

Python ile.

$ python -c '
import sys
with open(sys.argv[1]) as f:
    print " | ".join(line.strip() for line in f)' file

boşluklar printçok önemliydi.


2

İşte başka bir tane kullanıyor xxd

xxd -c1 -ps data | sed '$!s/0a/207c20/' | xxd -r -ps

2

Tamlık uğruna, işte başka bir awktabanlı çözüm, bu hiç kullanmıyor ORS:

awk 'BEGIN { ORS="" } { print p$0; p=" | " } END { print "\n" }' file > new_file

Açıklama için /unix//a/338121/117599 adresindeki gönderime bakın .

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.