Bir metin dosyasının satırlarındaki belirli kelimeler nasıl kaldırılır?


13

metin dosyam şöyle:

Liquid penetration 95% mass (m) = 0.000205348
Liquid penetration 95% mass (m) = 0.000265725
Liquid penetration 95% mass (m) = 0.000322823
Liquid penetration 95% mass (m) = 0.000376445
Liquid penetration 95% mass (m) = 0.000425341

şimdi Liquid penetration 95% mass (m)sadece değerleri elde etmek için satırlarımdan silmek istiyorum . Bunu nasıl yapmalıyım?


3
sadecegrep -o '[^[:space:]]\+$' file
Avinash Raj

@AvinashRaj: Şimdiye kadar, bu çözüm 'macun madalyası' alıyor :)
pa4080

2
@ pa4080 En azından test ettiğim giriş için (10M hatlar), Avinash Raj'ın genel yaklaşımı PCRE kullanılarak daha hızlı bir şekilde sipariş edilebilir. (GNU grep kabul gibi ben motor değil, desen, sorumlu olduğunu teyit olabilir \S+$ya ile -Eveya -P.) Bu türlü bir çözüm doğal olarak yavaş değil Yani. Ama yine de αғsнιη'ın cutyöntemine yakın bir yere ulaşamıyorum , bu da benchmarkınızı kazandı .
Eliah Kagan

Yanıtlar:


22

Yalnızca bir =işaret varsa, daha önce ve =bunun gibi her şeyi silebilirsiniz :

$ sed -r 's/.* = (.*)/\1/' file
0.000205348
0.000265725
0.000322823
0.000376445
0.000425341

Orijinal dosyayı değiştirmek istiyorsanız, -itest ettikten sonra seçeneği kullanın :

sed -ri 's/.* = (.*)/\1/' file

notlar

  • -rERE kullan, kaçmak zorunda değiliz (ve)
  • s/old/newyerine oldsahipnew
  • .* herhangi bir sayıda karakter
  • (things)kaydetmek thingssonradan ile bir geriye etmek \1, \2vb

Teşekkürler çalıştı. Bu komutu varolan dosyanın üzerine yazmak için kullandım: sed -i -r 's /.*= (. *) / \ 1 /' time.txt Nasıl çalıştığını açıklayabilir misiniz?
OE

Neden geri başvuruyu engellemiyorsunuz? s/^.*= //doğru değer satırın sonunda olduğu için eşit derecede iyi çalışır.
jpaugh

@jpaugh Kısmen ilk cevabım olan cevabımı değiştirmek için çok geç olduğu için - diğerleri zaten bahsettiğiniz çözümü ve bu dava için diğer daha etkili yolları verdiler :) Ama belki vs'nin nasıl kullanılacağını göstermek \1, böyle basit bir sorunu olmayan, arama yaparken bu soruya kara
Zanna

@Zanna En azından daha genel.
jpaugh

21

Bu bir iş awk; değerlerin yalnızca son alanda oluştuğunu varsayarsak (örneğinize göre):

awk '{print $NF}' file.txt
  • NFbir awkdeğişkendir, bir kayıttaki (satır) alan sayısına genişler, bu nedenle $NF( $öndeki not ) son alanın değerini içerir.

Misal:

% cat temp.txt 
Liquid penetration 95% mass (m) = 0.000205348
Liquid penetration 95% mass (m) = 0.000265725
Liquid penetration 95% mass (m) = 0.000322823
Liquid penetration 95% mass (m) = 0.000376445
Liquid penetration 95% mass (m) = 0.000425341

% awk '{print $NF}' temp.txt
0.000205348
0.000265725
0.000322823
0.000376445
0.000425341

13

Burada listelenen farklı çözümleri karşılaştırmaya karar verdim. Bu amaçla OP tarafından sağlanan içeriğe dayalı büyük bir dosya oluşturdum:

  1. Basit bir dosya oluşturdum input.file:

    $ cat input.file
    Liquid penetration 95% mass (m) = 0.000205348
    Liquid penetration 95% mass (m) = 0.000265725
    Liquid penetration 95% mass (m) = 0.000322823
    Liquid penetration 95% mass (m) = 0.000376445
    Liquid penetration 95% mass (m) = 0.000425341
    
  2. Sonra bu döngüyü yürüttüm:

    for i in {1..100}; do cat input.file | tee -a input.file; done
    
  3. Terminal penceresi engellendi. killall teeBaşka bir terminalden idam ettim . Sonra dosyanın içeriğini şu komutlarla inceledim: less input.fileve cat input.file. Son satır dışında iyi görünüyordu. Bu yüzden son satırı kaldırdım ve bir yedek kopya oluşturdum: cp input.file{,.copy}( inplace seçeneğini kullanan komutlar nedeniyle ).

  4. Dosyasına hatlarının son sayım input.fileolan 2 192 473 . Bu numarayı komutla aldım wc:

    $ cat input.file | wc -l
    2192473
    

İşte karşılaştırmanın sonucu:

  • grep -o '[^[:space:]]\+$'

    $ time grep -o '[^ [: boşluk:]] \ + $' input.file> output.file
    
    gerçek 0m58.539s
    kullanıcı 0m58.416s
    sys 0m0.108s
    
  • sed -ri 's/.* = (.*)/\1/'

    $ time sed -ri 's /.* = (. *) / \ 1 /' input.file
    
    gerçek 0m26.936s
    kullanıcı 0m22.836s
    sys 0m4.092s
    

    Alternatif olarak, çıktıyı yeni bir dosyaya yönlendirirsek, komut daha hızlıdır:

    $ time sed -r 's /.* = (. *) / \ 1 /' input.file> output.file
    
    gerçek 0m19.734s
    kullanıcı 0m19.672s
    sys 0m0.056s
    
  • gawk '{gsub(".*= ", "");print}'

    $ time gawk '{gsub (". * =", ""); print}' input.file> output.file
    
    gerçek 0m5.644s
    kullanıcı 0m5.568s
    sys 0m0.072s
    
  • rev | cut -d' ' -f1 | rev

    $ time rev input.file | kes -d '' -f1 | rev> output.file
    
    gerçek 0m3.703s
    kullanıcı 0m2.108s
    sys 0m4.916s
    
  • grep -oP '.*= \K.*'

    $ time grep -oP '. * = \ K. *' input.file> output.file
    
    gerçek 0m3.328s
    kullanıcı 0m3.252s
    sys 0m0.072s
    
  • sed 's/.*= //' (sırasıyla -iseçenek komutu birkaç kez yavaşlatır)

    $ time sed 's /.*= //' input.file> output.file
    
    gerçek 0m3.310s
    kullanıcı 0m3.212s
    sys 0m0.092s
    
  • perl -pe 's/.*= //' ( -iseçenek, buradaki verimlilikte büyük bir fark yaratmaz)

    $ time perl -i.bak -pe 's /.*= //' input.file
    
    gerçek 0m3.187s
    kullanıcı 0m3.128s
    sys 0m0.056s
    
    $ time perl -pe 's /.*= //' input.file> output.file
    
    gerçek 0m3.138s
    kullanıcı 0m3.036s
    sys 0m0.100s
    
  • awk '{print $NF}'

    $ time awk '{print $ NF}' input.file> output.file
    
    gerçek 0m1.251s
    kullanıcı 0m1.164s
    sys 0m0.084s
    
  • cut -c 35-

    $ time cut -c 35- input.file> output.file
    
    gerçek 0m0.352s
    kullanıcı 0m0.284s
    sys 0m0.064s
    
  • cut -d= -f2

    $ time cut -d = -f2 giriş dosyası> çıkış dosyası
    
    gerçek 0m0.328s
    kullanıcı 0m0.260s
    sys 0m0.064s
    

Fikrin kaynağı.


2
böylece benim cut -d= -f2çözüm kazanır. haha
αғsнιη

Bu dosyayı nasıl oluşturduğunuz hakkında daha fazla bilgi verebilir misiniz? Ayrıca, wc -lüç sayı nasıl çıkarılır? Başka seçenek iletilmediğinde, -lseçenek satır sayısı hariç her şeyi bastırmalıdır.
Eliah Kagan

@EliahKagan, bitti. Cevabı güncelledim.
pa4080

Ah, görüyorum - boşluklar rakam grubu ayırıcılarıydı. ( wcBu alanları gerçekten göstermiş miydi ? Bunu yapacak yerel ayarlar var mı?) Güncelleme için teşekkürler!
Eliah Kagan

@EliahKagan: Sonunda wcbir kez daha sorularınızı okudum . Fikirlerimin bugün erken nerede olduğunu bilmiyorum, ama onları gerçekten anlayamadım. Gerçekten de boşluklar basamak grubu ayırıcılarıydı ve wconları
eklemiyor

12

İle grepve -Psahip için PCRE, (a olarak desen yorumlama p erl- ompatible R egular e XPression) -otek başına baskı eşleştirilir model. Bildirim \K, kendisinden önce gelen eşleşen kısmı görmezden gelir.

$ grep -oP '.*= \K.*' infile
0.000205348
0.000265725
0.000322823
0.000376445
0.000425341

Veya cutbunun yerine komutu kullanabilirsiniz .

cut -d= -f2 infile

2
Çalıştırmanın yanı sıra en hızlı test tüm yöntemlerin pa4080 en kriter , bu cevap yöntemi aynı zamanda kesin kazanan koştum küçük kriter olduğunu test daha az yöntem ancak daha büyük bir girdi dosyasını kullandı. Kişisel olarak sevdiğim yöntemin hızlı varyantından on kat daha hızlıydı (ve cevabım esas olarak hakkında). cut
Eliah Kagan

11

Satır öneki her zaman aynı uzunluğa (34 karakter) sahip olduğundan şunları kullanabilirsiniz cut:

cut -c 35- < input.txt > output.txt

6

Dosyanın içeriğini tersine çevirin, revçıktıyı cutsınırlayıcı ve 1'i hedef alan olarak ekleyin, ardından orijinal numarayı almak için tekrar ters çevirin:

$ rev your_file | cut -d' ' -f1 | rev
0.000205348
0.000265725
0.000322823
0.000376445
0.000425341

5

Bu basit, kısa ve yazması, anlaması ve kontrol etmesi kolaydır ve ben şahsen beğendim:

grep -oE '\S+$' file

grepUbuntu'da , -Eveya ile çağrıldığında -P, bir boşluk karakteri (pratikte genellikle boşluk veya sekme) anlamına gelen ve olmayan bir şey anlamına gelen stenoyu alır . Kullanılması niceleyici ve sonu hattı çapa , desen bir satırın sonunda bir veya daha fazla olmayan boşlukları maçları . Bunun yerine kullanabilirsiniz ; bu durumda anlam aynıdır, ancak farklı bir düzenli ifade motoru kullanılır, bu nedenle farklı performans özelliklerine sahip olabilirler .\s\S+$\S+$-P-E

Bu Avinash Raj'ın yorumlu çözümüyle eşdeğerdir (sadece daha kolay, daha kompakt bir sözdizimiyle):

grep -o '[^[:space:]]\+$' file

Bu yaklaşımlar , sayıdan sonra boşluk bırakılabilirse işe yaramaz . Değiştirilebilirler, böylece yaparlar, ama burada buna girmenin bir anlamı yok. Bazen bir çözümü daha fazla vakada çalışmak için genelleştirmek öğretici olsa da, bunu insanların varsaymaya başladığı kadar sık ​​yapmak pratik değildir, çünkü kişinin genellikle sorunun hangi farklı uyumsuz yollardan hangisinin neye ihtiyacı olabileceğini bilmesinin bir yolu yoktur . genelleştirilmek.


Performans bazen önemli bir husustur. Bu soru, girdinin çok büyük olduğunu ve burada yayınlanan her yöntemin yeterince hızlı olduğunu öngörmemektedir. Ancak, hız isteniyorsa, on milyon satırlık giriş dosyasında küçük bir karşılaştırma:

$ perl -e 'print((<>) x 2000000)' file > bigfile
$ du -sh bigfile
439M    bigfile
$ wc -l bigfile
10000000 bigfile
$ TIMEFORMAT=%R
$ time grep -o '[^[:space:]]\+$' bigfile > bigfile.out
819.565
$ time grep -oE '\S+$' bigfile > bigfile.out
816.910
$ time grep -oP '\S+$' bigfile > bigfile.out
67.465
$ time cut -d= -f2 bigfile > bigfile.out
3.902
$ time grep -o '[^[:space:]]\+$' bigfile > bigfile.out
815.183
$ time grep -oE '\S+$' bigfile > bigfile.out
824.546
$ time grep -oP '\S+$' bigfile > bigfile.out
68.692
$ time cut -d= -f2 bigfile > bigfile.out
4.135

Siparişin önemli olması durumunda iki kez koştum (bazen I / O-ağır görevler için olduğu gibi) ve sonuçları çarptırabilecek arka planda başka şeyler yapmayan bir makinem olmadığı için. Bu sonuçlardan, en azından geçici olarak ve kullandığım boyuttaki girdi dosyaları için aşağıdakileri sonuçlandırıyorum:

  • Vaov! Geçiş -P( PCRE kullanmak için ) yerine -G(lehçe belirtilmediğinde varsayılan) veya bir büyüklük sırasına göre daha hızlı -Eyapılır grep. Bu nedenle, büyük dosyalar için bu komutu kullanmak, yukarıda gösterilen komuttan daha iyi olabilir:

    grep -oP '\S+$' file
  • VAY!! cutYöntem αғsнιη cevabı , daha hızlı Yolumdan daha hızlı sürümden daha bir büyüklük sırasına bitti! Pa4080'in karşılaştırmalı değerlendirmesinde de kazanan , bundan daha fazla yöntem içeren ancak daha küçük girdilerle - ve bu yüzden testime dahil etmek için diğer tüm yöntemlerden seçtim. Performans önemliyse veya dosyalar çok büyükse , bence αғsнιη'ın yöntemi kullanılmalıdır.cut -d= -f2 filecut

    Bu aynı zamanda , basit cutve pasteyardımcı programların unutulmaması ve belki de uygulanabilir olduğunda tercih edilmesi gerektiğini hatırlatan bir hizmet olarak hizmet eder , buna rağmen daha grepbirinci sınıf çözümler olarak sunulan daha karmaşık araçlar olsa da (kişisel olarak daha alışkın olduğum) kullanarak).


4

perl- s desen yerini tutmaz /.*= /boş dize ile //:

perl -pe 's/.*= //' input.file > output.file
perl -i.bak -pe 's/.*= //' input.file
  • Gönderen perl --help:

    -e program        one line of program (several -e's allowed, omit programfile)
    -p                assume loop like -n but print line also, like sed
    -i[extension]     edit <> files in place (makes backup if extension supplied)
    

sed - kalıbı boş dizeyle değiştirin:

sed 's/.*= //' input.file > output.file

veya (ama yukarıdakilerden daha yavaş) :

sed -i.bak 's/.*= //' input.file
  • Bu yaklaşımdan bahsediyorum, çünkü Zanna'nın cevabından birkaç kat daha hızlı .

gawk- kalıbı ".*= "boş dizeyle değiştirin "":

gawk '{gsub(".*= ", "");print}' input.file > output.file
  • Gönderen man gawk:

    gsub(r, s [, t]) For each substring matching the regular expression r in the string t,
                     substitute the string s, and return the number of substitutions. 
                     If t is not supplied, use $0...
    
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.