Risposte:
La chiave per far funzionare tutto ciò è dire sed
di escludere ciò che non si desidera produrre e specificare ciò che si desidera.
string='This is a sample 123 text and some 987 numbers'
echo "$string" | sed -rn 's/[^[:digit:]]*([[:digit:]]+)[^[:digit:]]+([[:digit:]]+)[^[:digit:]]*/\1 \2/p'
Questo dice:
-n
)p
)In generale, in sed
te acquisisci gruppi usando le parentesi e produci ciò che catturi usando un riferimento indietro:
echo "foobarbaz" | sed 's/^foo\(.*\)baz$/\1/'
produrrà "bar". Se si utilizza -r
( -E
per OS X) per regex esteso, non è necessario sfuggire alle parentesi:
echo "foobarbaz" | sed -r 's/^foo(.*)baz$/\1/'
Possono esserci fino a 9 gruppi di acquisizione e i loro riferimenti posteriori. I riferimenti posteriori sono numerati nell'ordine in cui appaiono i gruppi, ma possono essere utilizzati in qualsiasi ordine e possono essere ripetuti:
echo "foobarbaz" | sed -r 's/^foo(.*)b(.)z$/\2 \1 \2/'
genera "a bar a".
Se hai GNU grep
(potrebbe funzionare anche in BSD, incluso OS X):
echo "$string" | grep -Po '\d+'
o variazioni come:
echo "$string" | grep -Po '(?<=\D )(\d+)'
L' -P
opzione abilita le espressioni regolari compatibili Perl. Vedi man 3 pcrepattern
o man
3 pcresyntax
.
sed
all'esempio, se usi l' -r
opzione (o -E
per OS X, IIRC) non devi sfuggire alle parentesi. La differenza è tra espressioni regolari di base ed espressioni regolari estese ( -r
).
Sed ha fino a nove schemi ricordati, ma è necessario usare parentesi sfuggite per ricordare parti dell'espressione regolare.
Vedi qui per esempi e maggiori dettagli
sed -e 's/version=\(.+\)/\1/' input.txt
questo genererà comunque l'intero input.txt
\+
invece di +
. E non capisco perché le persone usano -e
solo un comando sed.
sed -e -n 's/version=\(.+\)/\1/p' input.txt
vedi: mikeplate.com/2012/05/09/…
sed -E
per usare le espressioni cosiddette "moderne" o "estese" che sembrano molto più vicine a Perl / Java / JavaScript / Go / qualunque sia il sapore. (Confronta con grep -E
o egrep
.) La sintassi predefinita ha quelle strane regole di escape ed è considerata "obsoleta". Per ulteriori informazioni sulle differenze tra i due, eseguire man 7 re_format
.
puoi usare grep
grep -Eow "[0-9]+" file
o
opzione è lì - unixhelp.ed.ac.uk/CGI/man-cgi?grep : -o, --only matching Mostra solo la parte di una riga corrispondente che corrisponde a PATTERN
grep -Eow -e "[0-9]+" -e "[abc]{2,3}"
non so come potresti richiedere che queste due espressioni siano su una riga a parte il piping di un grep precedente (che potrebbe comunque non funzionare se uno dei pattern corrisponde più di una volta su una riga ).
Questa risposta funziona con qualsiasi conteggio dei gruppi di cifre. Esempio:
$ echo 'Num123that456are7899900contained0018166intext' |
> sed -En 's/[^0-9]*([0-9]{1,})[^0-9]*/\1 /gp'
123 456 7899900 0018166
C'è un modo per dire a sed di produrre solo gruppi acquisiti?
Sì. sostituisci tutto il testo con il gruppo di acquisizione:
$ echo 'Number 123 inside text' | sed 's/[^0-9]*\([0-9]\{1,\}\)[^0-9]*/\1/'
123
s/[^0-9]* # several non-digits
\([0-9]\{1,\}\) # followed by one or more digits
[^0-9]* # and followed by more non-digits.
/\1/ # gets replaced only by the digits.
O con sintassi estesa (meno backquotes e consenti l'uso di +):
$ echo 'Number 123 in text' | sed -E 's/[^0-9]*([0-9]+)[^0-9]*/\1/'
123
Per evitare di stampare il testo originale in assenza di numeri, utilizzare:
$ echo 'Number xxx in text' | sed -En 's/[^0-9]*([0-9]+)[^0-9]*/\1/p'
E per abbinare più numeri (e anche stamparli):
$ echo 'N 123 in 456 text' | sed -En 's/[^0-9]*([0-9]+)[^0-9]*/\1 /gp'
123 456
Funziona con qualsiasi conteggio delle esecuzioni delle cifre:
$ str='Test Num(s) 123 456 7899900 contained as0018166df in text'
$ echo "$str" | sed -En 's/[^0-9]*([0-9]{1,})[^0-9]*/\1 /gp'
123 456 7899900 0018166
Che è molto simile al comando grep:
$ str='Test Num(s) 123 456 7899900 contained as0018166df in text'
$ echo "$str" | grep -Po '\d+'
123
456
7899900
0018166
e modello:
/([\d]+)/
Sed non riconosce la sintassi '\ d' (scorciatoia). L'equivalente ascii usato sopra [0-9]
non è esattamente equivalente. L'unica soluzione alternativa è usare una classe di caratteri: '[[: digit:]] `.
La risposta selezionata utilizza tali "classi di caratteri" per creare una soluzione:
$ str='This is a sample 123 text and some 987 numbers'
$ echo "$str" | sed -rn 's/[^[:digit:]]*([[:digit:]]+)[^[:digit:]]+([[:digit:]]+)[^[:digit:]]*/\1 \2/p'
Questa soluzione funziona solo per (esattamente) due serie di cifre.
Naturalmente, poiché la risposta viene eseguita all'interno della shell, possiamo definire un paio di variabili per ridurre tale risposta:
$ str='This is a sample 123 text and some 987 numbers'
$ d=[[:digit:]] D=[^[:digit:]]
$ echo "$str" | sed -rn "s/$D*($d+)$D+($d+)$D*/\1 \2/p"
Ma, come è già stato spiegato, s/…/…/gp
è meglio usare un comando:
$ str='This is 75577 a sam33ple 123 text and some 987 numbers'
$ d=[[:digit:]] D=[^[:digit:]]
$ echo "$str" | sed -rn "s/$D*($d+)$D*/\1 /gp"
75577 33 123 987
Ciò riguarderà sia le ripetute serie di cifre sia la scrittura di un breve comando (er).
Credo che lo schema indicato nella domanda fosse solo a scopo esemplificativo e l'obiettivo era quello di abbinare qualsiasi schema.
Se hai un sed con l'estensione GNU che consente l'inserimento di una nuova riga nello spazio del pattern, un suggerimento è:
> set string = "This is a sample 123 text and some 987 numbers"
>
> set pattern = "[0-9][0-9]*"
> echo $string | sed "s/$pattern/\n&\n/g" | sed -n "/$pattern/p"
123
987
> set pattern = "[a-z][a-z]*"
> echo $string | sed "s/$pattern/\n&\n/g" | sed -n "/$pattern/p"
his
is
a
sample
text
and
some
numbers
Questi esempi sono con tcsh (sì, lo so è la shell sbagliata) con CYGWIN. (Modifica: per bash, rimuovi set e gli spazi intorno a =.)
+
, dovresti fuggire o usare l' -r
opzione ( -E
per OS X). Puoi anche usare \{1,\}
(o -r
o -E
senza la fuga).
Rinuncia e usa Perl
Dal momento che sed
non lo taglia, buttiamo semplicemente l'asciugamano e usiamo Perl, almeno è LSB mentre le grep
estensioni GNU non sono :-)
Stampa l'intera parte corrispondente, nessun gruppo corrispondente o lookbehind necessario:
cat <<EOS | perl -lane 'print m/\d+/g'
a1 b2
a34 b56
EOS
Produzione:
12
3456
Corrispondenza singola per riga, campi dati spesso strutturati:
cat <<EOS | perl -lape 's/.*?a(\d+).*/$1/g'
a1 b2
a34 b56
EOS
Produzione:
1
34
Con lookbehind:
cat <<EOS | perl -lane 'print m/(?<=a)(\d+)/'
a1 b2
a34 b56
EOS
Campi multipli:
cat <<EOS | perl -lape 's/.*?a(\d+).*?b(\d+).*/$1 $2/g'
a1 c0 b2 c0
a34 c0 b56 c0
EOS
Produzione:
1 2
34 56
Partite multiple per riga, dati spesso non strutturati:
cat <<EOS | perl -lape 's/.*?a(\d+)|.*/$1 /g'
a1 b2
a34 b56 a78 b90
EOS
Produzione:
1
34 78
Con lookbehind:
cat EOS<< | perl -lane 'print m/(?<=a)(\d+)/g'
a1 b2
a34 b56 a78 b90
EOS
Produzione:
1
3478
Provare
sed -n -e "/[0-9]/s/^[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\).*$/\1 \2 \3 \4 \5 \6 \7 \8 \9/p"
Ho ottenuto questo sotto Cygwin:
$ (echo "asdf"; \
echo "1234"; \
echo "asdf1234adsf1234asdf"; \
echo "1m2m3m4m5m6m7m8m9m0m1m2m3m4m5m6m7m8m9") | \
sed -n -e "/[0-9]/s/^[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\).*$/\1 \2 \3 \4 \5 \6 \7 \8 \9/p"
1234
1234 1234
1 2 3 4 5 6 7 8 9
$
Non è ciò che l'OP ha richiesto (gruppi di acquisizione) ma è possibile estrarre i numeri utilizzando:
S='This is a sample 123 text and some 987 numbers'
echo "$S" | sed 's/ /\n/g' | sed -r '/([0-9]+)/ !d'
Dà quanto segue:
123
987
sed
l'attivazione di espressioni regolari estese con il-E
flag.