Bagaimana menghapus kata-kata tertentu dari baris file teks?


13

file teks saya terlihat seperti ini:

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

sekarang saya ingin menghapus Liquid penetration 95% mass (m)dari baris saya untuk mendapatkan nilai saja. Bagaimana saya harus melakukannya?


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

@AvinashRaj: Untuk saat ini, solusi ini mendapat 'medali dempul' :)
pa4080

2
@ pa4080 Setidaknya untuk input yang saya uji (garis 10M), pendekatan umum Avinash Raj dapat membuat urutan besarnya lebih cepat dengan menggunakan PCRE. (Saya dapat mengkonfirmasi bahwa mesin, bukan polanya, yang bertanggung jawab, karena GNU grep menerimanya \S+$dengan salah satu -Eatau -P.) Jadi, solusi semacam ini pada dasarnya tidak lambat. Tapi saya masih belum bisa mendekati metode αғsнιηcut , yang memenangkan benchmark Anda juga.
Eliah Kagan

Jawaban:


22

Jika hanya ada satu =tanda, Anda bisa menghapus semuanya sebelum dan termasuk =seperti ini:

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

Jika Anda ingin mengubah file asli, gunakan -iopsi setelah pengujian:

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

Catatan

  • -rgunakan ERE agar kita tidak perlu melarikan diri (dan)
  • s/old/newganti olddengannew
  • .* sejumlah karakter apa pun
  • (things)simpan thingske backreference kemudian dengan \1, \2, dll

Terima kasih itu berhasil. Saya menggunakan perintah ini untuk menimpa file yang ada: sed -i -r 's /.*= (. *) / \ 1 /' time.txt Bolehkah Anda menjelaskan bagaimana cara kerjanya?
OE

Mengapa tidak menghindari referensi balik? s/^.*= //akan bekerja sama baiknya, karena nilai yang benar adalah di akhir baris.
jpaugh

@ jpaugh Yah sebagian karena sudah terlambat untuk mengubah jawaban saya yang merupakan yang pertama diposting - yang lain telah memberikan solusi yang Anda sebutkan dan cara lain yang lebih efisien untuk kasus ini :) Tapi mungkin menunjukkan cara menggunakan \1dll memiliki beberapa nilai bagi orang-orang yang dapatkan pertanyaan ini saat mencari, yang tidak memiliki masalah sederhana
Zanna

@Zanna Ini lebih umum, setidaknya.
jpaugh

21

Ini adalah pekerjaan untuk awk; dengan asumsi nilai hanya terjadi di bidang terakhir (sesuai contoh Anda):

awk '{print $NF}' file.txt
  • NFadalah awkvariabel, memperluas ke jumlah bidang dalam catatan (baris), maka $NF(perhatikan $di depan) berisi nilai bidang terakhir.

Contoh:

% 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

Saya memutuskan untuk membandingkan berbagai solusi, yang tercantum di sini. Untuk tujuan ini, saya telah membuat file besar, berdasarkan konten yang disediakan oleh OP:

  1. Saya membuat file sederhana, bernama 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. Lalu saya mengeksekusi loop ini:

    for i in {1..100}; do cat input.file | tee -a input.file; done
    
  3. Jendela terminal diblokir. Saya dieksekusi killall teedari terminal lain. Kemudian saya memeriksa isi file dengan perintah: less input.fileand cat input.file. Itu terlihat bagus, kecuali baris terakhir. Jadi saya menghapus baris terakhir dan membuat salinan cadangan: cp input.file{,.copy}(karena perintah yang menggunakan opsi inplace ).

  4. Hitungan akhir baris ke dalam file input.fileadalah 2 192 473 . Saya mendapat nomor itu dengan perintah wc:

    $ cat input.file | wc -l
    2192473
    

Ini adalah hasil perbandingan:

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

    $ time grep -o '[^ [: space:]] \ + $' input.file> output.file
    
    0m58.539s nyata
    pengguna 0m58.416s
    sys 0m0.108s
    
  • sed -ri 's/.* = (.*)/\1/'

    $ time sed -ri 's /.* = (. *) / \ 1 /' input.file
    
    0m26.936s asli
    pengguna 0m22.836s
    sys 0m4.092s
    

    Atau jika kita mengarahkan output ke file baru perintah lebih cepat:

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

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

    $ time rev input.file | cut -d '' -f1 | rev> output.file
    
    0m3.703s nyata
    pengguna 0m2.108s
    sys 0m4.916s
    
  • grep -oP '.*= \K.*'

    $ time grep -oP '. * = \ K. *' input.file> output.file
    
    0m3.328s nyata
    pengguna 0m3.252s
    sys 0m0.072s
    
  • sed 's/.*= //' (masing-masing -iopsi membuat perintah beberapa kali lebih lambat)

    $ time sed /.*= // 'input.file> output.file
    
    0m3.310s nyata
    pengguna 0m3.212s
    sys 0m0.092s
    
  • perl -pe 's/.*= //' ( -iopsi tidak menghasilkan perbedaan besar dalam produktivitas di sini)

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

    $ time awk '{print $ NF}' input.file> output.file
    
    0m1.251s asli
    pengguna 0m1.164s
    sys 0m0.084s
    
  • cut -c 35-

    $ time cut -c 35- input.file> output.file
    
    0m0.352s nyata
    pengguna 0m0.284s
    sys 0m0.064s
    
  • cut -d= -f2

    $ time cut -d = -f2 input.file> output.file
    
    0m0.328s nyata
    pengguna 0m0.260s
    sys 0m0.064s
    

Sumber ide.


2
jadi solusi sayacut -d= -f2 menang. haha
αғsнιη

Bisakah Anda memberikan informasi lebih lanjut tentang cara Anda membuat file ini? Juga, bagaimana cara wc -lmenghasilkan tiga angka? Ketika tidak ada opsi lain yang dilewati, -lopsi harus menekan segalanya kecuali jumlah baris.
Eliah Kagan

@ ElliKagan, selesai. Saya sudah memperbarui jawabannya.
pa4080

Ah, saya mengerti - spasi adalah pemisah grup digit. (Apakah wcbenar-benar menampilkan ruang-ruang itu? Apakah ada pengaturan lokal yang akan melakukan itu?) Terima kasih atas pembaruannya!
Eliah Kagan

@EliahKagan: Akhirnya saya membaca pertanyaan Anda tentang wcsekali lagi. Aku tidak tahu di mana akalku lebih awal hari ini, tapi aku benar-benar tidak bisa memahaminya. Jadi memang spasi adalah pemisah grup digit , dan wctidak menambahkannya :)
pa4080

12

Dengan grepdan -Puntuk memiliki PCRE(Menafsirkan pola sebagai P erl- C ompatible R egular E Xpression) dan -opola untuk mencetak cocok saja. The \Kmemberitahukan akan mengabaikan bagian cocok datang sebelum itu sendiri.

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

Atau Anda bisa menggunakan cutperintah sebagai gantinya.

cut -d= -f2 infile

2
Selain menjalankan tercepat dari semua metode diuji dalam patokan pa4080 ini , yang cutmetode dalam jawaban ini juga pemenang dalam patokan kecil aku berlari yang diuji metode yang lebih sedikit tetapi menggunakan file input yang lebih besar. Itu lebih dari sepuluh kali lebih cepat daripada varian cepat dari metode yang saya suka secara pribadi (dan bahwa jawaban saya terutama tentang).
Eliah Kagan

11

Karena awalan garis selalu memiliki panjang yang sama (34 karakter), Anda dapat menggunakan cut:

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

6

Membalikkan isi file dengan rev, menyalurkan output cutdengan spasi sebagai pembatas dan 1 sebagai bidang target, lalu balikkan lagi untuk mendapatkan nomor asli:

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

5

Ini sederhana, pendek, dan mudah untuk ditulis, dipahami, dan diperiksa, dan saya pribadi menyukainya:

grep -oE '\S+$' file

grepdi Ubuntu , ketika dipanggil dengan -Eatau -P, mengambil singkatan \s artinya karakter spasi (dalam praktiknya biasanya spasi atau tab) dan \Sberarti apa pun yang bukan. Menggunakan quantifier+ dan anchor end-of-line$ , polanya \S+$cocok dengan satu atau lebih non-blanko di akhir baris . Anda bisa menggunakannya -Psebagai ganti -E; artinya dalam hal ini adalah sama tetapi mesin ekspresi reguler yang berbeda digunakan, sehingga mereka mungkin memiliki karakteristik kinerja yang berbeda .

Ini sama dengan solusi yang dikomentari Avinash Raj (hanya dengan sintaks yang lebih mudah dan lebih ringkas):

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

Pendekatan-pendekatan ini tidak akan berhasil jika ada jejak spasi setelah nomor. Mereka dapat dimodifikasi sehingga mereka lakukan, tapi saya tidak melihat ada gunanya masuk ke sini. Meskipun kadang-kadang instruktif untuk menggeneralisasi solusi untuk bekerja di bawah lebih banyak kasus, itu tidak praktis untuk melakukannya sesering orang cenderung berasumsi, karena orang biasanya tidak memiliki cara untuk mengetahui di mana dari banyak cara berbeda yang tidak cocok masalah mungkin pada akhirnya perlu untuk disamaratakan.


Kinerja terkadang menjadi pertimbangan penting. Pertanyaan ini tidak menetapkan bahwa inputnya sangat besar, dan kemungkinan setiap metode yang telah diposting di sini cukup cepat. Namun, jika kecepatan diinginkan, inilah patokan kecil pada file masukan sepuluh juta baris:

$ 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

Saya menjalankannya dua kali untuk berjaga-jaga jika urutannya penting (seperti yang kadang-kadang berlaku untuk tugas I / O-berat) dan karena saya tidak memiliki mesin yang tersedia yang tidak melakukan hal-hal lain di latar belakang yang dapat membelokkan hasilnya. Dari hasil-hasil tersebut saya menyimpulkan yang berikut, setidaknya untuk sementara dan untuk input file dengan ukuran yang saya gunakan:

  • Wow! Lewat-P (untuk menggunakan PCRE ) daripada -G(default ketika tidak ada dialek ditentukan) atau -Edibuat greplebih cepat dengan lebih dari satu urutan besarnya. Jadi untuk file besar, mungkin lebih baik menggunakan perintah ini daripada yang ditunjukkan di atas:

    grep -oP '\S+$' file
  • WOW!! The cutmetode dalam jawaban αғsнιη ini ,adalah lebih urutan besarnya lebih cepat daripada versi lebih cepat dari cara saya! Itu adalah pemenang dalam benchmark pa4080 juga, yang mencakup lebih banyak metode daripada ini tetapi dengan input yang lebih kecil - dan itulah sebabnya saya memilihnya, dari semua metode lain, untuk disertakan dalam pengujian saya. Jika kinerja penting atau file besar, saya pikir metode αғsнιη harus digunakan.cut -d= -f2 filecut

    Ini juga berfungsi sebagai pengingat bahwa utilitas sederhana cutdan tidak boleh dilupakanpaste , dan mungkin harus lebih disukai jika berlaku, meskipun ada alat yang lebih canggih seperti grepyang sering ditawarkan sebagai solusi lini pertama (dan bahwa saya secara pribadi lebih terbiasa dengan untuk menggunakan).


4

perl- s mengganti pola /.*= /dengan string kosong //:

perl -pe 's/.*= //' input.file > output.file
perl -i.bak -pe 's/.*= //' input.file
  • Dari 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 - gantikan pola dengan string kosong:

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

atau (tetapi lebih lambat dari yang di atas) :

sed -i.bak 's/.*= //' input.file
  • Saya menyebutkan pendekatan ini, karena beberapa kali lebih cepat daripada yang ada di jawaban Zanna .

gawk- gantikan pola ".*= "dengan string kosong "":

gawk '{gsub(".*= ", "");print}' input.file > output.file
  • Dari 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...
    
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.