dapatkah kita mencetak kata terakhir dari setiap baris di linux menggunakan perintah sed?


9

misalkan, jika ada file yang terdiri dari baris berikut, jika ada

12345 567 7878 66

   er3 t45t y6y46y 


 4y6 y656y y5y

   46y6 65y7 y66uyuy

 yy46y6y

Outputnya harus seperti:

66

y6y46y

yy

y66uyuyy

y46y6y

Saya telah mencoba sed 's/.* //g'nama file perintah dan beberapa sedperintah lain , tetapi tidak berfungsi.

Bisakah saya tahu apa sedperintah tepatnya ?


Apakah itu suatu keharusan untuk digunakan sed?
coffeMug

Jawaban:


8
awk '{print $NF}'
sed 's/[[:blank:]]*$//;s/.*[[:blank:]]//'

Itu masih akan mencetak baris kosong untuk setiap baris kosong. Untuk menghindarinya:

awk 'NF{print $NF}'
sed 's/[[:blank:]]*$//;s/.*[[:blank:]]//;/./!d'

Tunggal ekspresi alternatif: sed -n 's/.*[[:blank:]]\+\([^[:blank:]]\+\)[[:blank:]]*$/\1/p'.
jimmij

@ jimmij - yang tidak berfungsi jika urutan terakhir yang tidak kosong juga yang pertama dan tidak ada yang kosong sebelumnya. Juga, Anda mungkin hanya melakukannya .*di bagian ekor, mungkin - Anda mengesampingkan apa pun kecuali tertinggal kosong w / .*[^[:blank:]].
mikeserv


4

Anda dapat mencoba :

  • sed 's/.* //'
  • awk '{print $NF}'

4

Kamu hampir sampai. Cukup tentukan kata terakhir:

sed 's/^.* \([^ ][^ ]*\)/\1/g'

Apa fungsinya:

  1. '^. *' menghapus semua yang ada di awal baris dan spasi apa pun.
  2. '\ (...) \' cocok dengan pola dan mengembalikannya sebagai \ 1.
  3. '[^]' cocok dengan apa pun tanpa spasi di dalamnya.

(Diedit untuk menambahkan solusi yang lebih baik. Terima kasih Hildred!)


1
Berikut adalah ungkapan yang lebih pendek: sed -r 's/.* ([^ ]+)/\1/g'jika ekspresi reguler yang diperluas diizinkan, yang biasanya merupakan kasusnya.
mkalkov

Versi yang lebih pendek, dengan mengganti apa yang tidak ingin Anda simpan lebih dari yang ingin Anda simpan:sed 's/.* //'
Uriel

2

Anda dapat menggunakan beberapa pola yang cukup grepsebagai ganti sed, misalnya:

grep -o "[a-Z0-9]*$"

Dalam contoh ini, [...]rentang karakter yang dianggap sesuai untuk "kata" (alfanumerik dalam kasus ini, simbol lain dapat ditambahkan, beberapa di antaranya harus diloloskan).


2
Itu mengasumsikan tidak ada yang kosong di akhir baris. a-Zsebagai jangkauan tidak masuk akal, bahkan di lokal berbasis ASCII. Perhatikan bahwa itu -oadalah ekstensi GNU.
Stéphane Chazelas

0

Jika Anda memenuhi syarat kata yang berarti urutan 1 atau lebih karakter tidak kosong maka jawabannya pasti ya, dan itu sangat sederhana dilakukan juga. Ini karena [[:blank:]]*dan [^[:blank:]]*merupakan pelengkap boolean dan - asalkan semua karakter dalam sebuah string lengkap - [[:blank:]]*U [^[:blank:]]*dapat menggambarkan string apa pun yang mungkin dengan cara yang sama .*.

Jika karakter yang tidak lengkap atau urutan byte yang tidak valid ada dalam suatu string, tidak satu pun dapat berhasil menggambarkannya secara langsung - seperti yang kadang-kadang dapat terjadi ketika menafsirkan string dalam pengkodean yang salah. Untuk memastikan karakter lengkap per byte dalam string apa pun, C locale dapat dipaksa seperti:

LC_ALL=C sed ...

... yang akan menghindari masalah apa pun yang menggambarkan string dari kepala ke ekor dengan pola semua termasuk seperti .*atau([ ]*[^ ]*)*

Sebuah pola yang saling melengkapi dapat diulang sebanyak yang diperlukan dari kiri ke kanan sepanjang tali untuk mendarat pada kemungkinan yang terakhir terjadi tanpa ada kerusakan pada pola. Ini, secara pasti, adalah bahasa reguler.

BRE:

sed 's/\(\([^[:blank:]]*\)[[:blank:]]*\)*/\2/'

SEBELUM:

sed -E 's/(([^[:blank:]]*)[[:blank:]]*)*/\2/'

Kedua versi ini masih akan mencetak garis kosong, dan ini karena *bintang Kleene cocok dengan nol atau lebih kemunculan pola. Pertama-tama cocok dengan nol atau lebih bukan karakter kosong, lalu nol atau lebih karakter kosong, lalu nol atau lebih dari kecocokan yang dikelompokkan hingga cocok dengan string secara keseluruhan.

Setelah mencocokkan semua ini, keajaiban terjadi dalam penggantian - referensi dikembalikan oleh kelompok \1dan \2merupakan kejadian terakhir masing-masing. Jadi ketika penggantian dilakukan semua string diganti dengan hanya kejadian terakhir pada garis nol atau lebih bukan karakter kosong - atau subkelompok \2.

Tentu saja ini berfungsi untuk string apa pun - bahkan yang kosong - yang berarti kedua formulir akan mencetak karakter baris baru untuk baris yang hanya berisi karakter kosong atau tidak sama sekali. Untuk mengatasinya ada beberapa hal yang dapat Anda lakukan, tetapi pertama-tama mari kita buat kelas karakter sedikit lebih mudah untuk diketik:

b='[:blank:]'

Sekarang, untuk mencetak hanya jika satu baris berisi satu atau lebih karakter tidak kosong yang dapat Anda lakukan:

BRE:

sed -n "s/\(\([^$b]*\)[$b]*\)*/\2/;/./p"

SEBELUM:

sed -En "/[^$b]/s/(([^$b]*)[$b]*)*/\2/p"
  1. Kasur BRE - penggantian selalu dilakukan dan hanya ruang pola dengan setidaknya satu karakter tersisa yang dicetak.
  2. Kasing ERE - penggantian hanya dilakukan pada ruang pola yang berisi setidaknya satu char tidak kosong.

Formulir mana pun akan bekerja dengan metode mana pun - selama sintaksinya benar.

The -nmenonaktifkan saklar otomatis mencetak ruang pola, dan pbendera ke s///ubstitution atau /alamat /perintah mencetak hasil-hasilnya hanya jika berhasil.

Logika yang sama ini dapat diterapkan untuk mendapatkan {num}kejadian apa pun , juga, seperti:

BRE:

sed -n "s/\([$b]*\([^$b]\{1,\}\)\)\{num\}.*/\2/p"

SEBELUM:

sed -En "s/([$b]*([^$b]+)){num}.*/\2/p"

... di mana numdi kedua regexps dapat diganti dengan angka untuk hanya mencetak {num}kemunculan yang ditentukan dari urutan karakter yang tidak kosong. Bentuk yang sedikit berbeda digunakan di sini untuk memastikan penghitungan tidak condong untuk memimpin spasi dalam string.

Perhatikan bahwa -Esakelar ERE untuk seddidukung dalam versi BSD dan GNU, meskipun belum sintaks standar POSIX.


Penjelasan yang bagus, hack yang bagus, tetapi perhatikan itu tidak akan bekerja dengan implementasi tradisional sed (seperti Solaris / usr / bin / sed) dan akan lebih mahal daripada pendekatan yang lebih mudah (menghabiskan memori dengan jalur input lebih dari 25 karakter dengan misalnya sed_su3dari toolchest Heirloom). Jadi, meskipun saya suka jawabannya, saya tidak akan merekomendasikan pendekatan itu.
Stéphane Chazelas

Tampaknya juga tidak berfungsi di FreeBSD.
Stéphane Chazelas

@ StéphaneChazelas - yeah, kinerjanya benar-benar buruk untuk hal seperti ini, tetapi ini bisa sangat efektif untuk memilih kejadian bernomor. Dan untuk end case garis s/.* \([^[:blank:]]\{1,\}\).*/\1/jauh lebih baik, tetapi lebih sulit ketika beberapa baris terlibat. Namun, beberapa hari yang lalu, saya menemukan 's/\(\n\)*/\1/g;s/\n\(\n.*\)*/&&/[num];s///[samenum]bahwa secara efektif dapat menopang itu. Bagaimanapun, selama tidak ada kesalahan mencolok dalam logika maka saya senang - saya hanya berpikir saya pasti melewatkan sesuatu.
mikeserv

@ StéphaneChazelas - oh, dan tentang yang lebih tua sed- itu agak aneh - itu harus terdengar sesuai dengan standar. xrat mengatakan ... Pengembang standar menganggap perilaku historis umum, yang mendukung "\n*", tetapi tidak "\n\{min,max\}", "\(...\)*", atau "\(...\)\{min,max\}", sebagai hasil yang tidak disengaja dari implementasi tertentu, dan mereka mendukung duplikasi dan interval ekspresi berikut subekspresi dan referensi-kembali.
mikeserv

@ StéphaneChazelas - Dan standar mengatakan ... Jika subekspresi yang dirujuk oleh referensi-belakang cocok dengan lebih dari satu string karena tanda bintang ( '*' )atau ekspresi interval (lihat item (5)), referensi-belakang harus cocok dengan yang terakhir (paling kanan) ) dari string ini. Saya cukup yakin saya menguji ini minisedmeskipun - tentu saja saya sedang menguji sesuatu yang aneh minisedbeberapa hari yang lalu.
mikeserv

-1

Iya. Perintah sed berikut pertama-tama menghapus semua spasi spasi ( s/ *$//) dan kemudian semuanya hingga dan termasuk spasi putih terakhir ( s/.* //). Mungkin perlu mengganti spasi putih dengan [[:blank:]]untuk menangkap tab dan karakter seperti ruang lainnya.

$ echo "  aaa bbb cc   " | sed -e 's/ *$//' -e 's/.* //'
cc
$ echo "  aaa bbb cc" | sed -e 's/ *$//' -e 's/.* //'
cc
$ echo "aaa bbb cc   " | sed -e 's/ *$//' -e 's/.* //'
cc
$ echo "aaa bbb cc" | sed -e 's/ *$//' -e 's/.* //'
cc
$ echo "  cc  " | sed -e 's/ *$//' -e 's/.* //'
cc
$ echo "cc" | sed -e 's/ *$//' -e 's/.* //'
cc

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.