Como fazer um regex (sed) substituindo todas as ocorrências de um caractere enquanto exclui o último?

Como fazer um regex (sed) substituindo todas as ocorrências de um caractere enquanto exclui o último?

Estou lutando com o seguinte. Estou usando comandos como este no meu terminal Mac para testar meu regex:

echo 'inputstring' | sed (-E) '/s///g'

Estou tentando criar uma regex que:

  • se e somente se uma palavra terminar com a letra 'o', então:
  • exclui este 'o' final de palavra
  • substitui todas as ocorrências da letra 'i' por 'a' nesta palavra

Neste caso, a string de entrada é filo fililo felo falee a saída esperada éfal falal fel fale

Posso fazer uma regex que faça tanto a exclusão quanto a substituição, mas não vejo como combiná-las. Se eu colocar uma semicoluna entre eles, não vejo como colocar a parte condicional.

Também estou tendo problemas para definir a posição do 'fim da palavra'. Eu usei \b, mas parece não funcionar (ao contrário $do final da string).

Responder1

Eu não usaria sedpara isso, mas se isso for um exercício para aprender sed, faça um loop assim:

sed -E 's/$/ /
  :a
  s/i([[:alnum:]]*o[^[:alnum:]])/a\1/
  ta
  s/([[:alnum:]]*)o([^[:alnum:]])/\1\2/
  ta
  s/ $//'
  • Na primeira linha adiciono um espaço em branco no final, para que possamos tratar o final da linha como qualquer final de palavra. A última linha remove esse espaço em branco posteriormente.
  • O scomando na linha 3 procura ocorrências de iem uma palavra que termina com oe a substitui por a. O tcomando retorna à marca :apara repetir isso para todas as palavras com iterminação o.
  • Agora a quinta linha remove o final oe outro loop. Observe que de uma palavra que termina com oo, ambos serão removidos; não está claro se isso é desejado.

Apenas para referências, utilizo uma sedversão que suporta a oopção do scomando que significa apenas preservar a parte correspondente e jogar fora o resto. Também conhece o \hsubstituto para substituir pelo conteúdo do espaço de espera. Isso torna a tarefa de uma linha:

sed -E ':a;h;s/([[:alnum:]]*)o($|[^[:alnum:]])/\1\2/o;T;y/i/a/;x;s//\h/;ba'

Responder2

Eiseria mais preciso e flexível para tal caso:

awk '{ for(i=1;i<=NF;i++) 
       if ($i~/o$/) { sub(/o$/,"",$i); gsub("i","a",$i) } }1' <<<"filo fililo felo fale"

A saída:

fal falal fel fale

AlternativaPitãoabordagem de linha de comando:

python -c 'import sys,re; s = sys.stdin.read().strip(); 
print(re.sub(r"\b(\S+)o\b", lambda m: m.group(1).replace("i","a"), s))' <<<"filo fililo felo fale"
fal falal fel fale

Responder3

Não tenho certeza se isso é possível sed(suspeito que provavelmente não), mas é muito fácil de fazer com Python! Aqui está um script que faz exatamente o que você deseja:

#!/usr/bin/env python2
# -*- coding: ascii -*-
"""modify_strings.py"""

import sys
import re
import fileinput

# Iterate over lines of input
# (either read from files or from stdin)
for line in fileinput.input():

    # Split each line into tokens and preserve whitespace
    tokens = re.split(r'(\s+)', line)

    # Iterate over tokens
    for token in tokens:

        # If a word ends in 'o' then
        # perform the desired transformation
        if token.endswith('o'):
            token = token[:-1].replace('i', 'a')

        # Print out each token
        sys.stdout.write(token)

Você pode executá-lo assim:

echo 'filo fililo felo fale' | python modify_strings.py

E produz a seguinte saída (conforme desejado):

fal falal fel fale

Se você realmente deseja sedse envolver, provavelmente poderá conseguir o que deseja aumentando-o com um pouco de script de shell. Isso pode ser parecido com o seguinte bashscript:

#!/usr/bin/env bash

# modify-strings.bash

for word in "$@"; do
    if grep -q 'o$' <<<"${word}"; then
        echo -n "${word} " | sed -e 's/i/a/g' -e 's/o$//';
    else
        echo -n "${word} ";
    fi;
done
echo

Você chamaria esse script assim:

bash modify-strings.bash filo fililo felo fale

informação relacionada