2009-11-14 12 views
87

Vedo molti esempi e pagine man su come fare cose come cercare e sostituire usando sed, awk o gawk.come usare sed, awk o gawk per stampare solo ciò che corrisponde?

Ma nel mio caso, ho un'espressione regolare che voglio eseguire su un file di testo per estrarre un valore specifico. Non voglio fare ricerche e sostituzioni. Questo viene chiamato da bash. Facciamo un esempio:

Esempio espressione regolare: file di input

.*abc([0-9]+)xyz.* 

Esempio:

a 
b 
c 
abc12345xyz 
a 
b 
c 

Semplice come questo suona, io non riesco a capire come chiamare sed/awk/gawk correttamente . Quello che speravo di fare, è da dentro il mio script bash sono:

myvalue=$(sed <...something...> input.txt) 

Le cose che ho provato sono:

sed -e 's/.*([0-9]).*/\\1/g' example.txt # extracts the entire input file 
sed -n 's/.*([0-9]).*/\\1/g' example.txt # extracts nothing 
+5

Wow ... la gente ha votato questa domanda -1? È davvero inappropriato per una domanda? –

+0

Sembra perfettamente appropriato, usando Regex e potenti utility da riga di comando come sed/awk o qualsiasi editor come vi, emacs o teco può essere più simile alla programmazione che usare solo una vecchia applicazione. IMO questo appartiene a SO più di SU. – Dereleased

+0

Forse è stato votato in ribasso perché nella sua forma iniziale non ha definito chiaramente alcune delle sue esigenze. Non funziona ancora, a meno che non si leggano i commenti dell'OP alle risposte (incluso quello che ho cancellato quando le cose andavano a pera). – pavium

risposta

38

mio sed (Mac OS X) non ha funzionato con +.Ho provato * invece e ho aggiunto p tag per partita stampa:

sed -n 's/^.*abc\([0-9]*\)xyz.*$/\1/p' example.txt 

Per l'adattamento almeno un carattere numerico, senza +, vorrei utilizzare:

sed -n 's/^.*abc\([0-9][0-9]*\)xyz.*$/\1/p' example.txt 
+0

Grazie, questo ha funzionato anche per me una volta usato * invece di +. –

+2

... e l'opzione "p" per stampare la partita, che non sapevo neanche. Grazie ancora. –

+2

Ho dovuto sfuggire al '+' e poi ha funzionato per me: 'sed -n 's /^.* abc \ ([0-9] \ + \) xyz. * $/\ 1/p'' –

15

Io uso perl per rendere questo più facile per me. per esempio.

perl -ne 'print $1 if /.*abc([0-9]+)xyz.*/' 

Questo esegue Perl, l'opzione -n istruisce Perl per leggere in una sola riga alla volta da STDIN ed eseguire il codice. L'opzione -e specifica l'istruzione da eseguire.

L'istruzione esegue un'espressione regolare sulla riga letta e, se corrisponde, stampa il contenuto della prima serie di barre ($1).

Si può fare anche più nomi di file alla fine. per esempio.

perl -ne 'print $1 if /.*abc([0-9]+)xyz.*/' example1.txt example2.txt

+0

Grazie, ma non abbiamo accesso a perl, motivo per cui stavo chiedendo di sed/awk/gawk. –

1

Se si desidera selezionare le linee poi striscia fuori i bit non si vuole:

egrep 'abc[0-9]+xyz' inputFile | sed -e 's/^.*abc//' -e 's/xyz.*$//' 

Seleziona sostanzialmente le linee che desiderate con egrep e quindi utilizza sed per togliere il bit prima e dopo il numero.

È possibile vedere in azione qui:

pax> echo 'a 
b 
c 
abc12345xyz 
a 
b 
c' | egrep 'abc[0-9]+xyz' | sed -e 's/^.*abc//' -e 's/xyz.*$//' 
12345 
pax> 

Aggiornamento: ovviamente se la situazione reale è più complessa, avrà bisogno del RE a me modificato. Per esempio, se hai sempre avuto un unico numero sepolta all'interno di zero o più non-numerici all'inizio e alla fine:

egrep '[^0-9]*[0-9]+[^0-9]*$' inputFile | sed -e 's/^[^0-9]*//' -e 's/[^0-9]*$//' 
+0

Interessante ... Quindi non esiste un modo semplice per applicare un'espressione regolare complessa e recuperare ciò che si trova nella (...) sezione? Perché mentre vedo quello che hai fatto prima con grep, poi con sed, la nostra situazione reale è molto più complessa di quella di "abc" e "xyz". L'espressione regolare viene utilizzata perché un sacco di testo diverso può apparire su entrambi i lati del testo che vorrei estrarre. –

+0

Sono sicuro che * è * un modo migliore se le RE sono davvero complesse. Forse se avessi fornito qualche esempio in più o una descrizione più dettagliata, potremmo adattare le nostre risposte. – paxdiablo

-3

Per awk. Vorrei utilizzare il seguente script:

/.*abc([0-9]+)xyz.*/ { 
      print $0; 
      next; 
      } 
      { 
      /* default, do nothing */ 
      } 
+0

che diventa simile al comportamento di grep ... – dmckee

+0

Questo non produce il valore numerico '([0-9 +])', questo produce l'intera riga. –

-3
gawk '/.*abc([0-9]+)xyz.*/' file 
+2

Questo non sembra funzionare. Stampa l'intera riga invece della partita. –

+0

nel file di input di esempio, tale modello è l'intera linea. destra??? se sai che il modello si troverà in un campo specifico: usa $ 1, $ 2 ecc. es. gawk '$ 1 ~ /.*abc([0-9]+)xyz.*/'file – ghostdog74

5

Se la versione di grep lo supporta è possibile utilizzare l'opzione -o di stampare solo la parte di qualsiasi linea che corrisponde al tuo regexp.

Se poi non Ecco il meglio sed ho potuto venire con:

sed -e '/[0-9]/!d' -e 's/^[^0-9]*//' -e 's/[^0-9]*$//' 

... che cancella/salta senza cifre e, per le linee rimanenti, rimuove tutti i caratteri iniziali e finali non numerici . (Sto solo supponendo che la tua intenzione sia estrarre il numero da ogni riga che ne contiene uno).

Il problema con qualcosa come:.

sed -e 's/.*\([0-9]*\).*/&/' 

.... o

sed -e 's/.*\([0-9]*\).*/\1/' 

... è che sed supporta solo match "greedy" ... così il primo * sarà abbinare il resto della linea. A meno che non si possa usare una classe di caratteri negata per ottenere una corrispondenza non avida ... o una versione di sed con estensioni compatibili con Perl o altre sue regex, non possiamo estrarre una corrispondenza di pattern precisa con lo spazio pattern (a linea).

+0

Puoi semplicemente combinare due dei tuoi comandi 'sed' in questo modo:' sed -n' s/[^ 0-9] * \ ([0-9] \ + \). */\ 1/p'' –

+0

In precedenza non conoscevo l'opzione -o su grep. Bello sapere. Ma stampa l'intera partita, non il "(...)". Quindi, se stai abbinando "abc ([[: digit:]] +) xyz" allora ottieni "abc" e "xyz" oltre alle cifre. –

-1

si può fare con il guscio

while read -r line 
do 
    case "$line" in 
     *abc*[0-9]*xyz*) 
      t="${line##abc}" 
      echo "num is ${t%%xyz}";; 
    esac 
done <"file" 
2

perl è la sintassi più pulito, ma se non si dispone di Perl (non sempre lì, ho capito), allora l'unico modo per utilizzare gawk e componenti di un'espressione regolare è utilizzare la funzione gensub.

gawk '/abc[0-9]+xyz/ { print gensub(/.*([0-9]+).*/,"\\1","g"); }' < file 

output del file di input campione sarà

12345 

Nota:. Gensub sostituisce l'intero regex (tra l'//), quindi è necessario mettere il * prima e dopo il ([ 0-9] +) per eliminare il testo prima e dopo il numero nella sostituzione.

+2

Una soluzione intelligente e praticabile se devi (o vuoi) usare gawk. Hai notato questo, ma per essere chiari: non-GNU awk non ha gensub(), e quindi non supporta questo. – cincodenada

+0

Bello! Tuttavia, potrebbe essere meglio usare 'match()' per accedere ai gruppi catturati. Vedi [la mia risposta] (http://stackoverflow.com/a/39075261/1983854) per questo. – fedorqui

28

È possibile utilizzare sed per fare questo

sed -rn 's/.*abc([0-9]+)xyz.*/\1/gp' 
  • -n non stampare la linea risultante
  • -r questo lo rende in modo da non avere la fuoriuscita del gruppo di cattura parens ().
  • \1 il gruppo di cattura partita
  • /g partita globale
  • /p stampa il risultato

ho scritto un tool per me stesso che rende questo più facile

rip 'abc(\d+)xyz' '$1' 
+2

Questa è di gran lunga la risposta migliore e più spiegata finora! –

+0

Con alcune spiegazioni, è molto più facile capire cosa c'è di sbagliato nel nostro problema. Grazie ! – r4phG

3

È possibile utilizzare awk con match() per accedere al gruppo catturato:

$ awk 'match($0, /abc([0-9]+)xyz/, matches) {print matches[1]}' file 
12345 

Questo tentativo corrisponde al modello abc[0-9]+xyz. Se lo fa, memorizza le sue sezioni nell'array matches, il cui primo elemento è il blocco [0-9]+. Dal match()restituisce la posizione del carattere, o indice, di dove inizia quella sottostringa (1, se inizia all'inizio della stringa), attiva l'azione print.


Con grep è possibile utilizzare un look-dietro e look-ahead:

$ grep -oP '(?<=abc)[0-9]+(?=xyz)' file 
12345 

$ grep -oP 'abc\K[0-9]+(?=xyz)' file 
12345 

Questo controlla il modello [0-9]+ quando si verifica all'interno abc e xyz e si limita a stampare le cifre.

Problemi correlati