Как использовать awk для замены текста другим текстом, содержащим символ «&»?

Я хочу заменить текст файла, используя awk. Пример скрипта:

#!/bin/bash

test_file = "test.md"
key = "KEY"
value = "- & -"

echo "AAA $key AAA" > "$test_file"
echo "Original: $(cat "$test_file")"

tmp=$(mktemp)
awk -v s = "$key" -v r = "$value" '{sub(s,r)}1' "$test_file" > "$tmp" && mv "$tmp" "$test_file"
echo "Updated: $(cat "$test_file")"

rm "$test_file"

Ожидаемый результат:

Оригинал: ААА КЛЮЧ ААА
Обновлено: AAA - & - AAA.

Однако выводится некорректно (вместо символа "&" остается "КЛЮЧ"):

Оригинал: ААА КЛЮЧ ААА
Обновлено: ААА – КЛЮЧ – ААА.

Я также попробовал способ, описанный ниже, но безуспешно.

awk '
BEGIN { s=ARGV[1]; r=ARGV[2]; delete ARGV[1]; delete ARGV[2]; }
{ sub(s,r) }1
END {}' "$key" "$value" "$test_file" > "$tmp" && mv "$tmp" "$test_file"

Замена «&» другими символами работает нормально. Есть какие-нибудь предложения по решению этого дела?


Пожалуйста, проигнорируйте команду sed в этом вопросе, так как у меня есть другие случаи, и awk подходит для моего случая, за исключением этой странной проблемы с «&».

При замене символ & представляет совпадающий текст sub. Этого нужно избежать. например: value='- \\& -'

William Pursell 04.04.2024 17:37

@WilliamPursell, спасибо! Я только что проверил это, и результат правильный. Однако значение моего реального случая взято из другого файла, я не могу вручную изменить его, чтобы выйти. Итак, я только что попробовал raw_value = "- & -" && value = "$(echo "$raw_value" | sed -e 's/&/\&/g')", но это не сработало. Я попробую еще тмр, или у вас есть какие-нибудь предложения?

Kjuly 04.04.2024 17:56

@GordonDavisson Кажется, что результат подстановки регулярных выражений sed не заставляет awk cmd работать при передаче его в качестве значения (я пробовал вчера вечером, буду читать дальше, когда у меня будет возможность). Решение, предоставленное Анухавой, решило мою проблему. В любом случае, благодарю Вас!

Kjuly 05.04.2024 01:19

@GordonDavisson Обновление: raw_value = "- & -" && value = "$(echo "$raw_value" | sed -e 's/&/\\\\&/g')" также обеспечивает работу замены awk. Хороший!

Kjuly 05.04.2024 01:33
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
5
180
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вы можете использовать awk вот так с заменой bash:

key = "KEY"
value = "- & -"

awk -v s = "$key" -v r = "${value//&/\\\\&}" '{sub(s,r)}1' <<< 'AAA KEY AAA'

AAA - & - AAA

Здесь "${value//&/\\\\&}" заменит & на \\&.

Оно работает! Кстати, есть ли еще какие-нибудь символы, на которые стоит обратить внимание?

Kjuly 04.04.2024 18:29

Только замена & имеет особое значение, но в регулярном выражении поиска их может быть много, например ., *, +, [, ], (, ) и т. д.

anubhava 04.04.2024 19:20

Пожалуйста, загляните на stackoverflow.com/a/37039138/548225, чтобы экранировать все специальные символы в строке поиска.

anubhava 04.04.2024 19:47

Я бы использовал GNU AWK для этой задачи следующим образом.

key = "KEY"
value = "- & -"
echo "AAA $key AAA" | awk --assign FS = "$key" --assign OFS = "$value" '{$1=$1;print}'

дает результат

AAA - & - AAA

Объяснение: я сообщаю GNU AWK, что разделителем полей является ключ, а разделителем выходных полей является значение, затем для каждой строки я запускаю перестроение ($1=$1) и print результат. Имейте в виду, что вам все равно нужно быть осторожными с символами особого значения внутри ключа. Отказ от ответственности: этот код заменяет все вхождения ключа значением, отличным от sub, которое вы используете.

Если вы хотите узнать больше о FS или OFS, прочитайте 8 мощных встроенных переменных Awk — FS, OFS, RS, ORS, NR, NF, FILENAME, FNR

(проверено в GNU Awk 5.1.0)

Другие вопросы по теме