Non ho bisogno dell'intera riga, ma solo della corrispondenza dell'espressione regolare


15

Devo semplicemente ottenere la corrispondenza da un'espressione regolare:

$ cat myfile.txt | SOMETHING_HERE "/(\w).+/"

L'output deve essere solo ciò che è stato abbinato, tra parentesi.

Non pensare di poter usare grep perché corrisponde all'intera riga.

Per favore fatemi sapere come fare.

Risposte:


12

2 cose:

  • Come affermato da @Rory, è necessaria l' -oopzione, quindi viene stampata solo la corrispondenza (anziché l'intera riga)
  • Inoltre, è necessario il -P dell'opzione per usare le espressioni regolari Perl, che includono elementi utili come Guarda avanti (?= ) e Guarda dietro (?<= ) , quelli cercano parti, ma in realtà non corrispondono e non le stampano.

Se si desidera abbinare solo la parte all'interno del parensis:

grep -oP '(?<=\/\()\w(?=\).+\/)' myfile.txt

se il file contiene la puntura /(a)5667/, grep stamperà 'a', perché:

  • /(vengono trovati da \/\(, ma poiché sono in uno sguardo dietro (?<= ) non vengono segnalati
  • aè abbinato \we viene quindi stampato (a causa di -o)
  • )5667/si trovano b < \).+\/, ma poiché sono in una prospettiva, (?= ) non vengono segnalati

18

Usa il -o opzione in grep.

Per esempio:

$ echo "foobarbaz" | grep -o 'b[aeiou]r'
bar

4
Buon dolore ... Hai idea di quante volte ho lottato con sedriferimenti arretrati per farlo?
Insyte,

10
L'opzione o grep / egrep restituisce solo ciò che corrisponde all'intera espressione regolare, non solo ciò che è in () come ha chiesto.
Kyle Brandt,

1
Tuttavia, questa è un'ottima cosa da sapere comunque :-)
Kyle Brandt il

2
@KyleBrandt: per abbinare solo una parte (ad esempio: le parentesi) è possibile contrassegnare il resto con uno sguardo in avanti o uno dietro: (? <=) E (? =)
DrYak

6
    sed -n "s/^.*\(captureThis\).*$/\1/p"

-n      don't print lines
s       substitute
^.*     matches anything before the captureThis 
\( \)   capture everything between and assign it to \1 
.*$     matches anything after the captureThis 
\1      replace everything with captureThis 
p       print it

4

Se vuoi solo ciò che è tra parentesi, hai bisogno di qualcosa che supporti l'acquisizione di sotto-partite (gruppi di acquisizione con nome o numerati). Non credo che grep o egrep possano farlo, perl e sed can. Ad esempio, con perl:

Se un file chiamato foo ha una linea che è la seguente:

/adsdds      /

E tu fai:

perl -nle 'print $1 if /\/(\w).+\//' foo

La lettera a viene restituita. Potrebbe non essere quello che vuoi però. Se ci dici che cosa stai cercando di abbinare, potresti ricevere un aiuto migliore. $ 1 è tutto ciò che è stato catturato nella prima serie di parentesi. $ 2 sarebbe il secondo set ecc.


Stavo solo cercando di abbinare ciò che è tra parentesi. Sembra passarlo a un perl o uno script php potrebbe essere la risposta.
Alex L

4

Dato che hai aggiunto la tua domanda come bash oltre a shell , esiste un'altra soluzione oltre a grep :

Bash ha il suo motore di espressione regolare dalla versione 3.0, usando il =~ operatore, proprio come Perl.

ora, dato il seguente codice:

#!/bin/bash
DATA="test <Lane>8</Lane>"

if [[ "$DATA" =~ \<Lane\>([[:digit:]]+)\<\/Lane\> ]]; then
        echo $BASH_REMATCH
        echo ${BASH_REMATCH[1]}
fi
  • Nota che devi invocarlo come bashe non solosh per ottenere tutte le estensioni
  • $BASH_REMATCH fornirà l'intera stringa corrispondente a tutta l'espressione regolare, quindi <Lane>8</Lane>
  • ${BASH_REMATCH[1]} darà la parte corrispondente al 1 ° gruppo, quindi solo 8

Caro @DrYak, spero che tu non stia analizzando XML con regex qui .. :)
joonas.fi

È anche peggio. Sto analizzando un orribile mix di dati XML e FASTA (che entrambi usano il >simbolo per scopi completamente diversi) come emesso dal software di allineamento su larga scala veloce SANSparallel . Naturalmente entrambi i formati sono vomitati interlacciati senza alcuna fuga. Quindi è impossibile lanciare qualche libreria XML standard a questo. E sto usando Bash regex a questo punto del codice perché ho solo bisogno di estrarre un paio di dati, e 2 regex fanno il lavoro molto meglio per me che scrivere un parser dedicato per questo casino. #LifeInBioinformatics
DrYak,

In altre parole: c'è un punto in cui estrarre 1 singolo numero è più semplice da fare con un regex rathan che ballare l'intero tango XML
DrYak,

Ah, capito! :)
joonas.fi il

2

Supponendo che il file contenga:

$ cat file
Text-here>xyz</more text

E vuoi i personaggi tra >e</ , puoi usare:

grep -oP '.*\K(?<=>)\w+(?=<\/)' file
sed -nE 's:^.*>(\w+)</.*$:\1:p' file
awk '{print(gensub("^.*>(\\w+)</.*$","\\1","g"))}' file
perl -nle 'print $1 if />(\w+)<\//' file

Tutto stamperà una stringa "xyz".

Se vuoi catturare le cifre di questa linea:

$ cat file
Text-<here>1234</text>-ends

grep -oP '.*\K(?<=>)[0-9]+(?=<\/)' file
sed -E 's:^.*>([0-9]+)</.*$:\1:' file
awk '{print(gensub(".*>([0-9]+)</.*","\\1","g"))}' file
perl -nle 'print $1 if />([0-9]+)<\//' file


Per me fondamentale è stato capire che non funzionava con sed. C'è un motivo per cui usi [0-9] + lì. :)
user27432

@ user27423 non lo fa, ma le classi di caratteri POSIX ( lettura dolorosa , piacevole lettura ) fare: echo 'Text-<here>1234</text>-ends' | sed -E 's|.*>([[:digit:]]+)<.*|\1|'. In alcuni casi (ad es. [0-9]Vs. [[:digit:]]) non aiutano la leggibilità, in altri penso che lo facciano (ad es. [ \t\n\r\f\v]Vs. [:space:]).
Samuel Harmer, il

0

Ciò realizzerà ciò che stai richiedendo, ma non credo sia ciò che desideri davvero. Ho messo la .*parte anteriore del regex per mangiare qualcosa prima della partita, ma questa è un'operazione avida, quindi corrisponde solo al penultimo \wcarattere nella stringa.

Si noti che è necessario sfuggire ai genitori e al +.

sed 's/.*\(\w\).\+/\1/' myfile.txt
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.