Как заменить сложный многострочный текст в скрипте bash?

Я считаю, что ваш сценарий немного запутан и демонстрирует непонимание того, что такое awk. Программы Awk состоят из списка

pattern { actions }

заявление. Каждый раз, когда читается новая запись/строка, awk будет обрабатывать эту строку, выполняя оператор действия шаблона -один за другим. Из того, что я вижу, похоже, вы представляете, что awk считывает весь файл в памяти, а затем вы можете обработать его, используя цикл for по количеству записей. Это не так.

Следующий awk, вероятно, сделает то, что вы просите:

awk '/missing/' inputfile > "missing.txt"

Здесь шаблон — /missing/, а действие — действие по умолчанию 1, равное {print $0}, или напечатать полную запись. Шаблон /missing/гласит: выберите все строки, содержащие подстроку «отсутствует»

.
-3
13.02.2021, 16:57
2 ответа

Во-первых, слово руководства. Этот «текст» на самом деле является языком разметки, таким как XML или аналогичный. Обработка таких сложных и нюансированных входных данных, как простой, несформированный текст, скорее всего, вызовет проблемы в долгосрочной перспективе. Я настоятельно рекомендую вместо этого использовать такой инструмент, как XMLStarlet или аналогичный.

При всем при этом одним из решений было бы использование переменных, как это предусмотрено (, например, )GNU awk:

awk -v target="$target" -v replacement="$replacement" '{ gsub(target, replacement, $0) } 1'

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

2
18.03.2021, 22:30

Вы упускаете из виду основной принцип работы sed. Это линейно-ориентированный редактор, поскольку он извлекает ввод построчно. В то время как вы просите его обработать многострочное регулярное выражение, которое, очевидно, никогда не совпадет.

Если у вас есть GNU sed, вы можете slurpвставить файл с помощью опции -zв sed. Он смотрит на разделитель записей NUL=\0, которого нет в текстовых файлах. Следовательно, он будет читать весь файл как одну длинную запись.

Нам нужно настроить целевые и замещающие переменные, поскольку они предположительно могут содержать символы, которые sed обрабатывает как регулярное выражение. Поэтому нам нужно экранировать их и только потом использовать в выражении sed.

srch=$(printf '%s\n' "$target" |
sed -e '
  H;1h;$!d;x
  s:[][\/^$*.]:\\&:g
  s/[[:space:]]\{1,\}/[[:space:]]\\{1,\\}/g
')

repl=$(printf '%s\n' "$replacement" |
sed -e '
  s:[\&/]:\\&:g
  $!s:$:\\:
')

sed -e '$!{' -e 'N;H;s/.*//;x;D' -e '}' -e "s/$srch/$repl/g" file.txt

Результаты:

<a>
    <b>
        <c>
            R-HELLO-1
        </c>
    </b>
</a>

<html>
    <head>
        <title>
            O-HELLO-2
        </title>
    </head>
</html>

в случае, если в вашей системе установлен perl, вы также можете использовать это; мы сопоставляем один и тот же скелет, но разное количество пробелов, потому что вы не хотите, чтобы один пробел испортил совпадение.

srch="$target"      \
repl="$replacement" \
perl -0777 -pe '
  (my $re = quotemeta $ENV{srch}) =~ s/(\\\s)+/\\s+/g;
  s/$re/$ENV{repl}/g;
' file.txt 
0
18.03.2021, 22:30

Теги

Похожие вопросы