¿Cómo hacer una expresión regular (sed) que reemplace todas las apariciones de un carácter mientras se elimina el último?

¿Cómo hacer una expresión regular (sed) que reemplace todas las apariciones de un carácter mientras se elimina el último?

Estoy luchando con lo siguiente. Estoy usando comandos como este en mi terminal Mac para probar mi expresión regular:

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

Estoy intentando crear una expresión regular que:

  • si y sólo si una palabra termina en la letra 'o', entonces:
  • elimina esta 'o' final de palabra
  • reemplaza todas las apariciones de la letra 'i' por 'a' en esta palabra

En este caso, la cadena de entrada es filo fililo felo faley la salida esperada esfal falal fel fale

Puedo crear una expresión regular que elimine o reemplace, pero no veo cómo combinarlas. Si pongo una semicolumna entre ellas, no veo cómo poner la parte condicional.

También tengo problemas para definir la posición "el final de la palabra". Lo usé \bpero no parece funcionar (a diferencia $del final de la cadena).

Respuesta1

Yo no lo usaría sedpara esto, pero si es un ejercicio para aprender sed, haz un bucle como este:

sed -E 's/$/ /
  :a
  s/i([[:alnum:]]*o[^[:alnum:]])/a\1/
  ta
  s/([[:alnum:]]*)o([^[:alnum:]])/\1\2/
  ta
  s/ $//'
  • En la primera línea agrego un espacio en blanco al final, para que podamos tratar el final de la línea como cualquier final de palabra. La última línea elimina ese espacio en blanco más adelante.
  • El scomando en la línea 3 busca apariciones de ien una palabra que termina en oy la reemplaza con a. El tcomando vuelve a marcar :apara repetir esto para todas las palabras que terminan ien todas o.
  • Ahora la quinta línea elimina el final oy otro bucle. Tenga en cuenta que a partir de una palabra que termina en oo, se eliminarán ambas; No está claro si esto es lo que se desea.

Solo como referencia, utilizo una sedversión que admite la oopción del scomando, lo que significa solo preservar la parte coincidente y desechar el resto. También conoce el \hreemplazo para reemplazar por el contenido del espacio de retención. Esto hace que la tarea sea de una sola línea:

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

Respuesta2

awkSería más preciso y flexible 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"

La salida:

fal falal fel fale

AlternativaPitónenfoque de línea 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

Respuesta3

No estoy seguro de si es posible hacer esto sed(sospecho que probablemente no lo sea), ¡pero es realmente fácil de hacer con Python! Aquí hay un script que hace exactamente lo que desea:

#!/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)

Puedes ejecutarlo así:

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

Y produce el siguiente resultado (según se desee):

fal falal

Si realmente desea sedparticipar, probablemente pueda obtener lo que desea aumentándolo con un poco de script de shell. Podría parecerse al siguiente 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

Llamarías a este script así:

bash modify-strings.bash filo fililo felo fale

información relacionada