GNU/Linux >> Linux Esercitazione >  >> Linux

Estrarre un'espressione regolare abbinata a "sed" senza stampare i caratteri circostanti?

A tutti i medici "sed" là fuori:

Come puoi ottenere 'sed' per estrarre un'espressione regolare che ha trovato una corrispondenza in una riga
?

In altre parole, voglio solo la stringa corrispondente all'espressione
regolare con tutti i caratteri non corrispondenti dalla riga che li contiene rimossi.

Ho provato a utilizzare la funzione di riferimento indietro come di seguito

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

questo funziona per alcune espressioni come

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

che estrae ordinatamente tutti i nomi delle macro che iniziano con 'CONFIG_ ….' (trovato in alcuni file '*.h') e li stampa tutti riga per riga

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

MA quanto sopra si scompone per qualcosa come

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

questo restituisce sempre cifre singole come

                 7
                 9
                 .
                 .  
                 6

piuttosto che estrarre un campo numerico contiguo come.

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

P.S.:Sarei grato a un feedback su come questo è stato ottenuto in 'sed'.
So come farlo con 'grep' e 'awk'
Vorrei scoprire se il mio – anche se limitato – la comprensione di
'sed' ha dei buchi e se c'è un modo per farlo in 'sed' che io
ho semplicemente trascurato.

Risposta accettata:

Quando un'espressione regolare contiene gruppi, potrebbe esserci più di un modo per confrontare una stringa:le espressioni regolari con i gruppi sono ambigue. Ad esempio, considera l'espressione regolare ^.*([0-9][0-9]*)$ e la stringa a12 . Ci sono due possibilità:

  • Abbina a contro .* e 2 contro [0-9]*; 1 corrisponde a [0-9] .
  • Abbina a1 contro .* e la stringa vuota contro [0-9]*; 2 corrisponde a [0-9] .

Sed, come tutti gli altri strumenti regexp disponibili, applica la prima regola di corrispondenza più lunga:prima cerca di abbinare la prima porzione di lunghezza variabile a una stringa che sia il più lunga possibile. Se trova un modo per abbinare il resto della stringa al resto dell'espressione regolare, bene. Altrimenti, sed prova la successiva corrispondenza più lunga per la prima porzione a lunghezza variabile e riprova.

Qui, la corrispondenza con la stringa più lunga per prima è a1 contro .* , quindi il gruppo corrisponde solo a 2 . Se vuoi che il gruppo inizi prima, alcuni motori regexp ti consentono di creare il .* meno avido, ma sed non ha una tale caratteristica. Quindi devi rimuovere l'ambiguità con qualche ancoraggio aggiuntivo. Specifica che il .* iniziale non può terminare con una cifra, in modo che la prima cifra del gruppo sia la prima corrispondenza possibile.

  • Se il gruppo di cifre non può trovarsi all'inizio della riga:

    sed -n 's/^.*[^0-9]([0-9][0-9]*).*/1/p'
    
  • Se il gruppo di cifre può trovarsi all'inizio della riga e il tuo sed supporta il ? operatore per parti opzionali:

    sed -n 's/^(.*[^0-9])?([0-9][0-9]*).*/1/p'
    
  • Se il gruppo di cifre può trovarsi all'inizio della riga, attenendosi ai costrutti regexp standard:

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

A proposito, è la stessa prima regola di corrispondenza più lunga che rende [0-9]* abbina le cifre dopo la prima, anziché il successivo .* .

Nota che se ci sono più sequenze di cifre su una riga, il tuo programma estrarrà sempre l'ultima sequenza di cifre, sempre a causa della prima regola di corrispondenza più lunga applicata al .* . Se vuoi estrarre la prima sequenza di cifre, devi specificare che quella che precede è una sequenza di non cifre.

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

Più in generale, per estrarre la prima corrispondenza di un'espressione regolare, è necessario calcolare la negazione di tale espressione regolare. Sebbene ciò sia sempre teoricamente possibile, la dimensione della negazione cresce esponenzialmente con la dimensione dell'espressione regolare che stai negando, quindi questo è spesso impraticabile.

Correlati:Impossibile abilitare il supporto SMART per il disco rigido esterno?

Considera il tuo altro esempio:

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

Questo esempio in realtà mostra lo stesso problema, ma non lo vedi sugli input tipici. Se lo dai in pasto hello CONFIG_FOO_CONFIG_BAR , quindi il comando sopra stampa CONFIG_BAR , non CONFIG_FOO_CONFIG_BAR .

C'è un modo per stampare la prima corrispondenza con sed, ma è un po' complicato:

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

(Supponendo che il tuo sed supporti n per indicare una nuova riga nelle s testo sostitutivo.) Funziona perché sed cerca la prima corrispondenza dell'espressione regolare e non cerchiamo di abbinare ciò che precede il CONFIG_… po. Poiché non c'è una nuova riga all'interno della riga, possiamo usarla come indicatore temporaneo. Il T il comando dice di rinunciare se il precedente s il comando non corrispondeva.

Quando non riesci a capire come fare qualcosa in sed, passa a awk. Il comando seguente stampa la prima corrispondenza più lunga di un'espressione regolare:

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

E se hai voglia di mantenerlo semplice, usa Perl.

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

Linux
  1. Manipolazione del testo da riga di comando con sed

  2. Aggiunta con 'sed'?

  3. Cosa c'è di sbagliato nella mia regex lookahead in GNU sed?

  4. Stampare dalla riga di comando con LibreOffice, comandi lpr?

  5. Perché sed non utilizza la modalità regex estesa per impostazione predefinita?

Sostituisci le virgolette intelligenti con il comando sed di Linux

Hai bisogno di sfuggire ai caratteri Regex in Sed per essere interpretato come caratteri Regex?

Contando i caratteri di ogni riga con Wc?

Aggiungere la parola alla fine della riga con Sed?

Usare il comando tr in Linux per giocare con i personaggi

Come sostituire in modo ricorsivo i caratteri con sed?