Respostas:
A chave para fazer isso funcionar é dizer sed
para excluir o que você não deseja exibir, bem como especificar o que você deseja.
string='This is a sample 123 text and some 987 numbers'
echo "$string" | sed -rn 's/[^[:digit:]]*([[:digit:]]+)[^[:digit:]]+([[:digit:]]+)[^[:digit:]]*/\1 \2/p'
Isto diz:
-n
)p
)Em geral, em sed
você captura grupos usando parênteses e gera o que captura usando uma referência anterior:
echo "foobarbaz" | sed 's/^foo\(.*\)baz$/\1/'
irá produzir "bar". Se você usar -r
( -E
para OS X) para regex estendido, não precisará escapar dos parênteses:
echo "foobarbaz" | sed -r 's/^foo(.*)baz$/\1/'
Pode haver até 9 grupos de captura e suas referências anteriores. As referências anteriores são numeradas na ordem em que os grupos aparecem, mas podem ser usadas em qualquer ordem e podem ser repetidas:
echo "foobarbaz" | sed -r 's/^foo(.*)b(.)z$/\2 \1 \2/'
gera "uma barra a".
Se você possui o GNU grep
(também pode funcionar no BSD, incluindo o OS X):
echo "$string" | grep -Po '\d+'
ou variações como:
echo "$string" | grep -Po '(?<=\D )(\d+)'
A -P
opção habilita expressões regulares compatíveis com Perl. Veja man 3 pcrepattern
ou man
3 pcresyntax
.
sed
exemplo, se usa a -r
opção (ou -E
para OS X, IIRC), não precisa escapar dos parênteses. A diferença é que entre expressões regulares básicas e expressões regulares estendidas ( -r
).
O Sed tem até nove padrões lembrados, mas você precisa usar parênteses escapados para lembrar partes da expressão regular.
Veja aqui exemplos e mais detalhes
sed -e 's/version=\(.+\)/\1/' input.txt
este ainda irá saída o todo input.txt
\+
vez de +
. E eu não entendo por que as pessoas usam -e
apenas um comando sed.
sed -e -n 's/version=\(.+\)/\1/p' input.txt
see: mikeplate.com/2012/05/09/…
sed -E
usar as chamadas expressões regulares "modernas" ou "estendidas" que parecem muito mais próximas aos tipos Perl / Java / JavaScript / Go / qualquer que seja. (Compare com grep -E
ou egrep
.) A sintaxe padrão possui essas regras de escape estranhas e é considerada "obsoleta". Para mais informações sobre as diferenças entre os dois, execute man 7 re_format
.
você pode usar grep
grep -Eow "[0-9]+" file
o
- é por isso que a opção existe - unixhelp.ed.ac.uk/CGI/man-cgi?grep : -o, --only-matching Mostra apenas a parte de uma linha correspondente que corresponde ao PATTERN
grep -Eow -e "[0-9]+" -e "[abc]{2,3}"
Não sei como você pode exigir que essas duas expressões estejam em uma linha, exceto a canalização de um grep anterior (que ainda não funcionaria se um dos padrões corresponder mais de uma vez em uma linha )
Esta resposta funciona com qualquer contagem de grupos de dígitos. Exemplo:
$ echo 'Num123that456are7899900contained0018166intext' |
> sed -En 's/[^0-9]*([0-9]{1,})[^0-9]*/\1 /gp'
123 456 7899900 0018166
Existe alguma maneira de dizer ao sed para produzir apenas grupos capturados?
Sim. substitua todo o texto pelo grupo de captura:
$ 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.
Ou com sintaxe estendida (menos aspas anteriores e permite o uso de +):
$ echo 'Number 123 in text' | sed -E 's/[^0-9]*([0-9]+)[^0-9]*/\1/'
123
Para evitar imprimir o texto original quando não houver número, use:
$ echo 'Number xxx in text' | sed -En 's/[^0-9]*([0-9]+)[^0-9]*/\1/p'
E para combinar vários números (e também imprimi-los):
$ echo 'N 123 in 456 text' | sed -En 's/[^0-9]*([0-9]+)[^0-9]*/\1 /gp'
123 456
Isso funciona para qualquer contagem de execuções de dígitos:
$ 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
O que é muito semelhante ao comando grep:
$ str='Test Num(s) 123 456 7899900 contained as0018166df in text'
$ echo "$str" | grep -Po '\d+'
123
456
7899900
0018166
e padrão:
/([\d]+)/
O Sed não reconhece a sintaxe '\ d' (atalho). O equivalente ascii usado acima [0-9]
não é exatamente equivalente. A única solução alternativa é usar uma classe de caracteres: '[[: digit:]] `.
A resposta selecionada usa essas "classes de caracteres" para criar uma solução:
$ str='This is a sample 123 text and some 987 numbers'
$ echo "$str" | sed -rn 's/[^[:digit:]]*([[:digit:]]+)[^[:digit:]]+([[:digit:]]+)[^[:digit:]]*/\1 \2/p'
Essa solução funciona apenas para (exatamente) duas execuções de dígitos.
Obviamente, como a resposta está sendo executada dentro do shell, podemos definir algumas variáveis para tornar essa resposta mais curta:
$ 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"
Mas, como já foi explicado, usar um s/…/…/gp
comando é melhor:
$ 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
Isso abrangerá repetidas execuções de dígitos e gravará um comando curto (er).
Eu acredito que o padrão dado na pergunta foi apenas a título de exemplo, e o objetivo era corresponder a qualquer padrão.
Se você tiver um sed com a extensão GNU permitindo a inserção de uma nova linha no espaço do padrão, uma sugestão é:
> 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
Estes exemplos estão com o tcsh (sim, eu sei que é o shell errado) com o CYGWIN. (Editar: para o bash, remova o conjunto e os espaços ao redor =.)
+
, você precisaria escapar dele ou usar a -r
opção ( -E
para OS X). Você também pode usar \{1,\}
(ou -r
ou -E
sem o escape).
Desista e use Perl
Como sed
não o corta, vamos jogar a toalha e usar o Perl, pelo menos é LSB, enquanto as grep
extensões GNU não são :-)
Imprima a peça correspondente inteira, sem grupos correspondentes ou olhando para trás:
cat <<EOS | perl -lane 'print m/\d+/g'
a1 b2
a34 b56
EOS
Resultado:
12
3456
Correspondência única por linha, geralmente campos de dados estruturados:
cat <<EOS | perl -lape 's/.*?a(\d+).*/$1/g'
a1 b2
a34 b56
EOS
Resultado:
1
34
Com lookbehind:
cat <<EOS | perl -lane 'print m/(?<=a)(\d+)/'
a1 b2
a34 b56
EOS
Vários campos:
cat <<EOS | perl -lape 's/.*?a(\d+).*?b(\d+).*/$1 $2/g'
a1 c0 b2 c0
a34 c0 b56 c0
EOS
Resultado:
1 2
34 56
Várias correspondências por linha, geralmente dados não estruturados:
cat <<EOS | perl -lape 's/.*?a(\d+)|.*/$1 /g'
a1 b2
a34 b56 a78 b90
EOS
Resultado:
1
34 78
Com lookbehind:
cat EOS<< | perl -lane 'print m/(?<=a)(\d+)/g'
a1 b2
a34 b56 a78 b90
EOS
Resultado:
1
3478
Experimentar
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"
Compreendi isso no 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
$
Não é o que o OP solicitou (capturando grupos), mas você pode extrair os números usando:
S='This is a sample 123 text and some 987 numbers'
echo "$S" | sed 's/ /\n/g' | sed -r '/([0-9]+)/ !d'
Fornece o seguinte:
123
987
sed
ativar expressões regulares estendidas com o-E
sinalizador.