Tentando excluir caracteres finais de uma palavra com sed regex

Tentando excluir caracteres finais de uma palavra com sed regex

Sou novo em regex e sed e estou tentando criar o que pensei que seria um regex simples: quero remover a letra final da palavra se for um 'o'.

  • String de entrada: Olá Olá
  • Resultado esperado: Inferno Inferno

A boa notícia: posso remover o 'o' quando estiver no final da string:

$ echo 'Hello Hello' |sed 's/\(.*\)o/\1/g'
Hello Hell
$ echo 'Hello Hello' |sed 's/\(.*\)o$/\1/g'
Hello Hell

A má notícia: não consigo removê-lo das palavras anteriores na string. Eu tentei isso com todos os símbolos de âncora que consigo imaginar. O resultado é que nenhum dos 'o's finais da palavra é removido:

$ echo 'Hello Hello' |sed 's/\(.*\)o\b/\1/g'
Hello Hello
$ echo 'Hello Hello' |sed 's/\(.*\)o\>/\1/g'
Hello Hello
$ echo 'Hello Hello' |sed 's/\(.*\)o\W/\1/g'
Hello Hello
$ echo 'Hello Hello' |sed 's/\(.*\)o\s/\1/g'
Hello Hello

Você pode me ajudar a recuperar minha sanidade, me dizendo o que estou fazendo de errado?

Atualização: tenho a nítida impressão de que minha máquina produz resultados diferentes dos de outras pessoas. Estou usando a janela do terminal no meu Macbook. Se alguém puder lançar alguma luz sobre isso, por favor me diga.

Responder1

echo 'Hello Hello' | sed 's/o$//'

parece mais útil para mim do que o seu

echo 'Hello Hello' | sed 's/\(.*\)o$/\1/g'

Na sua pergunta diz que a saída de

echo 'Hello Hello' | sed 's/\(.*\)o\b/\1/g'

foi Hello Hello, mas para mim é Hello Hell. Você pode corrigir isso para

echo 'Hello Hello' | sed 's/\([^o]*\)o\b/\1/g'

mas

echo 'Hello Hello' | sed 's/o\b//g'

parece melhor para mim.

Responder2

Remover o ono final das palavras é remover ao entre um caractere de palavra e um caractere não-palavra (ou EOL), portanto:

sed -r 's/(\w)o(\W|$)/\1\2/g'

Responder3

Estou me perguntando se de alguma forma spacesua palavra não é delimitadora. Tente algo como o seguinte:

$ echo hello hello | sed -e 's/o / /g;s/o$//'
hell hell

O problema com este exemplo é que você também terá que fazer o mesmo para .ee ,qualquer outro delimitador de palavra. Correspondência oseguida por outro caractere específico com []like o[ \.,]. Por algum motivo, isso não funciona para EOL $, então adicione outra string de pesquisa com ;. Exemplo:

$ echo hello hello, hello. toot hello | sed -e 's/o\([ \.,]\)/\1/g;s/o$//'
hell hell, hell. toot hell
$ echo $SHELL
/bin/bash
$ sed --version
sed (GNU sed) 4.4
$ set | grep IFS
IFS=$' \t\n'

Responder4

Eu tentei isso com todos os símbolos de âncora que consigo imaginar.

Não são as âncoras, mas o fato de você ter uma correspondência gananciosa com o asterisco. A \(.*\)oexpressão correspondeuma corda tão longa quanto possível, então ele vai comer tudo até odurar o. Pode corresponder oaos anteriores também.

Mas então, capturar algo e depois devolvê-lo é inútil, você pode simplesmente remover o \(.*\)e o \1completamente.

Então, estes (pelo menos no GNU sed) removeriam o's no final das palavras:

sed 's/o\>//g' 
sed 's/o\b//g' 

Isso, claro, apenas no final da string:

sed 's/o$//g' 

E isso removerá um o, junto com um seguinte caractere que não seja de palavra (por exemplo, o espaço depois de Hello):

sed 's/o\W//g' 

Se você sednão suportar \</ \>ou \b, você terá que fazer outra coisa. Isso corresponderia oseguido por um caractere não alfanumérico ou pelo final da linha:

$ echo "jello, jello" | sed -E -e 's/o([^[:alnum:]]|$)/\1/g'
jell, jell

Isso funciona, por exemplo, no sedque vem com o OS X/macOS.


As expressões regulares Perl suportam a adição de um ponto de interrogação *ou +para torná-las não gananciosas. Então eles combinariammais curtosequência possível:

echo "jello, jello" | perl -pe 's/(.*?)o/$1/g'
jell, jell

informação relacionada