Как создать регулярное выражение (sed), заменяющее все вхождения одного символа и удаляющее последний?

Как создать регулярное выражение (sed), заменяющее все вхождения одного символа и удаляющее последний?

Я борюсь со следующим. Я использую такие команды в терминале Mac, чтобы проверить свое регулярное выражение:

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

Я пытаюсь создать регулярное выражение, которое:

  • тогда и только тогда, когда слово заканчивается на букву «о», то:
  • удаляет это слово с конечной буквой «о»
  • заменяет все вхождения буквы «i» на «a» в этом слове

В этом случае входная строка filo fililo felo faleи ожидаемый выводfal falal fel fale

Я могу сделать регулярное выражение, которое делает либо удаление, либо замену, но не вижу, как их объединить. Если я поставлю между ними полустолбец, я не вижу, как вставить условную часть.

У меня также возникли проблемы с определением позиции 'конец слова'. Я использовал, \bно это, похоже, не работает (в отличие $от конца строки).

решение1

Я бы не стал использовать sedэто, но если это упражнение для обучения sed, сделайте такой цикл:

sed -E 's/$/ /
  :a
  s/i([[:alnum:]]*o[^[:alnum:]])/a\1/
  ta
  s/([[:alnum:]]*)o([^[:alnum:]])/\1\2/
  ta
  s/ $//'
  • В первой строке я добавляю пробел в конце, так что мы можем рассматривать конец строки как конец любого слова. Последняя строка позже удаляет этот пробел.
  • Команда sв строке 3 ищет вхождения iв слове, заканчивающемся на , oи заменяет его на a. tКоманда возвращается к метке, :aчтобы повторить это для всех iслов, oзаканчивающихся на .
  • Теперь пятая строка удаляет окончание oи еще одну петлю. Обратите внимание, что из слова, заканчивающегося на oo, они оба будут удалены; неясно, желательно ли это.

Только для справок я использую sedверсию, поддерживающую oопцию команды, sкоторая означает только сохранение совпавшей части и отбрасывание остального. Также она знает \hв замене, чтобы заменить содержимым удерживаемого пространства. Это делает задачу однострочной:

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

решение2

АвкДля такого случая было бы точнее и гибче:

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

Выход:

fal falal fel fale

АльтернативаПитонподход с использованием командной строки:

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

решение3

Я не уверен, возможно ли это сделать с помощью Python sed(подозреваю, что, скорее всего, нет), но это очень легко сделать с помощью Python! Вот скрипт, который делает именно то, что вам нужно:

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

Вы можете запустить его так:

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

И он выдает следующий вывод (как и требовалось):

фал фалал фал фале

Если вы действительно хотите sedучаствовать, то вы, вероятно, можете получить то, что хотите, дополнив это небольшим скриптингом оболочки. Это может выглядеть примерно так bash:

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

Вы могли бы вызвать этот скрипт так:

bash modify-strings.bash filo fililo felo fale

Связанный контент