sed: exclui texto entre uma string até a primeira ocorrência de outra string

sed: exclui texto entre uma string até a primeira ocorrência de outra string

Imagine que tenho algo como o seguinte texto:

A rápida raposa marrom salta em 2012 e 2013

E eu gostaria de excluir a parte de "fox" incluindo os quatro números, mas apenas na primeira ocorrência, para terminar com:

O marrom rápido e 2013

Algo assim...:

echo "A rápida raposa marrom salta em 2012 e 2013" \
   | sed "s/fox.*\([0-9]\{4\}\)//g"

... me traz:

O marrom rápido

Então removeu tudo, incluindo a última ocorrência dos quatro números.

Alguma ideia?

Responder1

Expressões regulares POSIX usadas por sed(ambas as versões "básica" e "estendida") não suportam correspondências não gananciosas. (Embora existam algumas soluções alternativas, como usar [^0-9]*no lugar de .*, elas se tornam não confiáveis ​​se as entradas variarem muito.)

O que você precisa pode ser alcançado em Perl usando o ?quantificador não ganancioso:

echo "The quick brown fox jumps in 2012 and 2013" \
   | perl -pe 's/fox.*?([0-9]{4})//g'

Você também pode querer remover um espaço extra.

Responder2

Supondo que você queira usarapenassed e você deseja que o final da correspondência seja o primeiro grupo de dígitos, sem se importar com a palavra após os dígitos, isso funciona:

echo "A rápida raposa marrom salta em 2012 e 2013" \
   | sed "s/fox[^0-9][^0-9]*[0-9][0-9]* //"

O padrão funciona combinando fox, seguido por um ou mais não dígitos [^0-9][^0-9]*, seguido por 1 ou mais dígitos [0-9][0-9]*. Este padrão funcionará com um número arbitrário de dígitos, não apenas 4. Se você quiser corresponder exatamente 4 dígitos, altere para:

echo "A rápida raposa marrom salta em 2012 e 2013" \
   | sed "s/fox[^0-9]*\([0-9]\{4\}\) //"

Responder3

Você não especificouexatamentequais são seus requisitos. Você pode querer um processo de várias etapas. Escolha uma string que você sabe que não ocorrerá em sua entrada (por exemplo, ####):

echo "A rápida raposa marrom saltou sobre 42 cães preguiçosos em 2012 e 2013." \
  | sed \
        -e "s/[0-9]\{4\}/&####/" \
        -e "s/raposa.*####//" \
        -e "s/####//"

(Comando excessivamente dobrado para facilitar a leitura.) As -e "s/[0-9]\{4\}/&####/"injeções ####apóso primeironúmero de quatro dígitos. (Aviso: isso mudará 65536para 6553####6.)
-e "s/fox.*####//"afeta linhas que contêm foxe ####- ou seja, linhas que contêm pelo menos um número de quatro dígitos - e depois exclui de foxatéo primeironúmero de quatro dígitos.
-e "s/####//", é claro, limpa todas ####as strings que sobraram de linhas que contêm um número de quatro dígitos, mas não fox.

Para remover também um espaço após o número, se houver,

echo "A rápida raposa marrom saltou sobre 42 cães preguiçosos em 2012 e 2013." \
  | sed \
        -e "s/[0-9]\{4\}/&####/" \
        -e "s/fox.*#### //"\
        -e "s/raposa.*####//" \
        -e "s/####//"

Aviso: você pode adicionar gtodos os scomandos, mas, como ainda usa .*, que é a raiz do seu problema, ainda não funcionará

One fox jumps in 2012 and 2013, another fox will jump in 2014 and 2015.

do jeito que você provavelmente deseja. E, claro, vocênãodeseja adicionar gporque "s/[0-9]\{4\}/&####/"então ele será injetado ####depoistodonúmero de quatro dígitos, anulando todo o ponto. Então "s/fox.*####//"acabará agindo exatamente como "s/fox.*[0-9]\{4\}//"(seu comando original com os caracteres não contribuintes removidos); ou seja, isso vai mudar

A rápida raposa marrom salta em 2012 e 2013.

para

A rápida raposa marrom salta em 2012#### e 2013####.

e então para

O marrom rápido.

informação relacionada