GNU/Linux >> Belajar Linux >  >> Linux

Mengekstrak Regex yang Dicocokkan Dengan 'sed' Tanpa Mencetak Karakter Sekitarnya?

Untuk semua dokter 'sed' di luar sana:

Bagaimana Anda bisa mendapatkan 'sed' untuk mengekstrak ekspresi reguler yang cocok dengannya dalam
baris?

Dengan kata lain, saya hanya ingin string yang sesuai dengan ekspresi
reguler dengan semua karakter yang tidak cocok dari baris yang berisi dihapus.

Saya mencoba menggunakan fitur back-reference seperti di bawah ini

regular expression to be isolated 
         gets `inserted` 
              here     
               |
               v  
 sed -n 's/.*( ).*/1/p 

ini berfungsi untuk beberapa ekspresi seperti

 sed -n 's/.*(CONFIG_[a-zA-Z0-9_]*).*/1/p 

yang dengan rapi mengekstrak semua nama makro yang dimulai dengan 'CONFIG_ ....' ( ditemukan di beberapa file '*.h' ) dan mencetak semuanya baris demi baris

          CONFIG_AT91_GPIO
          CONFIG_DRIVER_AT91EMAC
                   .
                   .   
          CONFIG_USB_ATMEL
          CONFIG_USB_OHCI_NEW
                   .
                 e.t.c. 

TAPI di atas rusak untuk sesuatu seperti

  sed -n 's/.*([0-9][0-9]*).*/1/p 

ini selalu mengembalikan satu digit seperti

                 7
                 9
                 .
                 .  
                 6

daripada mengekstrak bidang angka yang berdekatan seperti.

              8908078
              89670890  
                 .
                 .  
                 .
               23019   
                 .
               e.t.c.  

P.S.:Saya akan berterima kasih atas umpan balik tentang bagaimana ini dicapai di 'sed'.
Saya tahu bagaimana melakukan ini dengan 'grep' dan 'awk'
Saya ingin mencari tahu apakah saya – meskipun terbatas – pemahaman tentang
'sed' memiliki lubang di dalamnya dan jika ada cara untuk melakukan ini di 'sed' yang saya
abaikan.

Jawaban yang Diterima:

Saat regexp berisi grup, mungkin ada lebih dari satu cara untuk mencocokkan string dengannya:regexp dengan grup ambigu. Misalnya, pertimbangkan regexp ^.*([0-9][0-9]*)$ dan string a12 . Ada dua kemungkinan:

  • Cocokkan a terhadap .* dan 2 melawan [0-9]*; 1 dicocokkan dengan [0-9] .
  • Cocokkan a1 terhadap .* dan string kosong terhadap [0-9]*; 2 dicocokkan dengan [0-9] .

Sed, seperti semua alat regexp lainnya di luar sana, menerapkan aturan kecocokan terlama yang paling awal:pertama kali mencoba mencocokkan bagian panjang variabel pertama dengan string yang sepanjang mungkin. Jika menemukan cara untuk mencocokkan sisa string dengan sisa regexp, baiklah. Jika tidak, sed mencoba kecocokan terpanjang berikutnya untuk bagian panjang variabel pertama dan mencoba lagi.

Di sini, kecocokan dengan string terpanjang pertama adalah a1 terhadap .* , jadi grup hanya cocok dengan 2 . Jika Anda ingin grup dimulai lebih awal, beberapa mesin regexp memungkinkan Anda membuat .* kurang serakah, tetapi sed tidak memiliki fitur seperti itu. Jadi, Anda perlu menghilangkan ambiguitas dengan beberapa jangkar tambahan. Tentukan bahwa .* leading utama tidak dapat diakhiri dengan angka, sehingga angka pertama dari grup adalah kemungkinan pertama yang cocok.

  • Jika kelompok angka tidak boleh berada di awal baris:

    sed -n 's/^.*[^0-9]([0-9][0-9]*).*/1/p'
    
  • Jika grup digit dapat berada di awal baris, dan sed Anda mendukung ? operator untuk suku cadang opsional:

    sed -n 's/^(.*[^0-9])?([0-9][0-9]*).*/1/p'
    
  • Jika grup digit dapat berada di awal baris, tetap berpegang pada konstruksi regexp standar:

    sed -n -e 's/^.*[^0-9]([0-9][0-9]*).*/1/p' -e t -e 's/^([0-9][0-9]*).*/1/p'
    

Omong-omong, aturan kecocokan terlama yang sama yang membuat [0-9]* mencocokkan angka setelah yang pertama, bukan .* .

Perhatikan bahwa jika ada beberapa urutan digit pada satu baris, program Anda akan selalu mengekstrak urutan digit terakhir, lagi-lagi karena aturan pencocokan terpanjang paling awal diterapkan pada .* . Jika Anda ingin mengekstrak urutan digit pertama, Anda perlu menentukan bahwa apa yang muncul sebelumnya adalah urutan non-digit.

sed -n 's/^[^0-9]*([0-9][0-9]*).*$/1/p'

Lebih umum, untuk mengekstrak kecocokan pertama dari regexp, Anda perlu menghitung negasi dari regexp itu. Meskipun secara teori hal ini selalu memungkinkan, ukuran negasi tumbuh secara eksponensial dengan ukuran regexp yang Anda tolak, jadi ini sering kali tidak praktis.

Terkait:Tidak dapat mengaktifkan dukungan SMART untuk hard drive eksternal?

Pertimbangkan contoh Anda yang lain:

sed -n 's/.*(CONFIG_[a-zA-Z0-9_]*).*/1/p'

Contoh ini sebenarnya menunjukkan masalah yang sama, tetapi Anda tidak melihatnya pada input biasa. Jika Anda memberinya makan hello CONFIG_FOO_CONFIG_BAR , kemudian perintah di atas mencetak CONFIG_BAR , bukan CONFIG_FOO_CONFIG_BAR .

Ada cara untuk mencetak kecocokan pertama dengan sed, tetapi sedikit rumit:

sed -n -e 's/(CONFIG_[a-zA-Z0-9_]*).*/n1/' -e T -e 's/^.*n//' -e p

(Dengan asumsi sed Anda mendukung n berarti baris baru dalam s teks pengganti.) Ini berfungsi karena sed mencari kecocokan paling awal dari regexp, dan kami tidak mencoba mencocokkan apa yang mendahului CONFIG_… sedikit. Karena tidak ada baris baru di dalam baris, kita dapat menggunakannya sebagai penanda sementara. T perintah mengatakan untuk menyerah jika s sebelumnya perintah tidak cocok.

Saat Anda tidak tahu cara melakukan sesuatu di sed, beralihlah ke awk. Perintah berikut mencetak kecocokan terlama dari sebuah regexp:

awk 'match($0, /[0-9]+/) {print substr($0, RSTART, RLENGTH)}'

Dan jika Anda ingin membuatnya tetap sederhana, gunakan Perl.

perl -l -ne '/[0-9]+/ && print $&'       # first match
perl -l -ne '/^.*([0-9]+)/ && print $1'  # last match

Linux
  1. Hapus lima karakter pertama pada baris mana pun dari file teks di Linux dengan sed

  2. Temukan file dengan karakter windows ilegal dalam namanya di Linux

  3. Bagaimana saya bisa menggunakan grep untuk mencocokkan tetapi tanpa mencetak kecocokan?

  1. Pengelompokan regex cocok dengan pustaka regex C++ 11

  2. Mencetak dari baris perintah dengan LibreOffice, perintah lpr?

  3. Bagaimana cara mengganti karakter dengan sed secara rekursif?

  1. Ganti kutipan pintar dengan perintah sed Linux

  2. Tampilkan Semua File Hingga Pertandingan?

  3. Mengapa sed tidak menggunakan mode regex yang diperluas secara default?