Quando um regexp contém grupos, pode haver mais de uma maneira de corresponder uma string a ela: os regexps com grupos são ambíguos. Por exemplo, considere o regexp ^.*\([0-9][0-9]*\)$ e a string a12 . Existem duas possibilidades:
- Corresponde
aao.*e2ao[0-9]*;1é correspondido por[0-9]. - Corresponde
a1a.*e a string vazia a[0-9]*;2é correspondido por[0-9].
Sed, como todas as outras ferramentas de expressão regular, aplica a regra de correspondência mais longa mais antiga: primeiro tenta corresponder a primeira porção de tamanho variável a uma string que seja o maior tempo possível. Se encontrar uma maneira de combinar o restante da string com o resto do regexp, tudo bem. Caso contrário, sed tenta a próxima correspondência mais longa para a primeira parte de comprimento variável e tenta novamente.
Aqui, a correspondência com a string mais longa primeiro é a1 contra .* , portanto, o grupo corresponde apenas a 2 . Se você quiser que o grupo comece mais cedo, alguns mecanismos de expressão regular permitem deixar o .* menos ganancioso, mas o sed não tem esse recurso. Então você precisa remover a ambigüidade com alguma âncora adicional. Especifique que o .* inicial não pode terminar com um dígito, para que o primeiro dígito do grupo seja a primeira correspondência possível.
-
Se o grupo de dígitos não puder estar no começo da linha:
sed -n 's/^.*[^0-9]\([0-9][0-9]*\).*//p' -
Se o grupo de dígitos puder estar no início da linha, e seu sed oferecer suporte ao operador
\?para peças opcionais:sed -n 's/^\(.*[^0-9]\)\?\([0-9][0-9]*\).*//p' -
Se o grupo de dígitos puder estar no início da linha, atenha-se às construções regexp padrão:
sed -n -e 's/^.*[^0-9]\([0-9][0-9]*\).*//p' -e t -e 's/^\([0-9][0-9]*\).*//p'
A propósito, é a mesma regra de correspondência mais longa mais antiga que faz com que [0-9]* corresponda aos dígitos após o primeiro, em vez do .* subseqüente.
Observe que, se houver várias sequências de dígitos em uma linha, seu programa sempre extrairá a última sequência de dígitos, novamente devido à primeira regra de correspondência mais longa aplicada à inicial .* . Se você quiser extrair a primeira seqüência de dígitos, você precisa especificar que o que vem antes é uma seqüência de não dígitos.
sed -n 's/^[^0-9]*\([0-9][0-9]*\).*$//p'
Geralmente, para extrair a primeira correspondência de um regexp, é necessário calcular a negação desse regexp. Embora isso sempre seja teoricamente possível, o tamanho da negação cresce exponencialmente com o tamanho do regexp que você está negando, portanto, isso geralmente é impraticável.
Considere o seu outro exemplo:
sed -n 's/.*\(CONFIG_[a-zA-Z0-9_]*\).*//p'
Este exemplo realmente exibe o mesmo problema, mas você não o vê em entradas típicas. Se você alimentar hello CONFIG_FOO_CONFIG_BAR , o comando acima imprimirá CONFIG_BAR , não CONFIG_FOO_CONFIG_BAR .
Existe uma maneira de imprimir o primeiro jogo com sed, mas é um pouco complicado:
sed -n -e 's/\(CONFIG_[a-zA-Z0-9_]*\).*/\n/' -e T -e 's/^.*\n//' -e p
(Assumindo que seu sed suporta \n para significar uma nova linha no texto de substituição de s .) Isso funciona porque o sed procura a correspondência mais antiga do regexp e não tentamos corresponder ao que precede o CONFIG_… pouco. Como não há nova linha dentro da linha, podemos usá-la como marcador temporário. O comando T diz para desistir se o comando s anterior não corresponder.
Quando você não consegue descobrir como fazer algo em sed, vire-se para awk. O comando a seguir imprime a correspondência mais antiga mais antiga de um regexp:
awk 'match($0, /[0-9]+/) {print substr($0, RSTART, RLENGTH)}'
E se você quiser mantê-lo simples, use Perl.
perl -l -ne '/[0-9]+/ && print $&' # first match
perl -l -ne '/^.*([0-9]+)/ && print $1' # last match