Я хочу заменить текст файла, используя 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
подходит для моего случая, за исключением этой странной проблемы с «&».
@WilliamPursell, спасибо! Я только что проверил это, и результат правильный. Однако значение моего реального случая взято из другого файла, я не могу вручную изменить его, чтобы выйти. Итак, я только что попробовал raw_value = "- & -" && value = "$(echo "$raw_value" | sed -e 's/&/\&/g')"
, но это не сработало. Я попробую еще тмр, или у вас есть какие-нибудь предложения?
@GordonDavisson Кажется, что результат подстановки регулярных выражений sed не заставляет awk cmd работать при передаче его в качестве значения (я пробовал вчера вечером, буду читать дальше, когда у меня будет возможность). Решение, предоставленное Анухавой, решило мою проблему. В любом случае, благодарю Вас!
@GordonDavisson Обновление: raw_value = "- & -" && value = "$(echo "$raw_value" | sed -e 's/&/\\\\&/g')"
также обеспечивает работу замены awk. Хороший!
Вы можете использовать awk
вот так с заменой bash:
key = "KEY"
value = "- & -"
awk -v s = "$key" -v r = "${value//&/\\\\&}" '{sub(s,r)}1' <<< 'AAA KEY AAA'
AAA - & - AAA
Здесь "${value//&/\\\\&}"
заменит &
на \\&
.
Оно работает! Кстати, есть ли еще какие-нибудь символы, на которые стоит обратить внимание?
Только замена &
имеет особое значение, но в регулярном выражении поиска их может быть много, например ., *, +, [, ], (, )
и т. д.
Пожалуйста, загляните на stackoverflow.com/a/37039138/548225, чтобы экранировать все специальные символы в строке поиска.
Я бы использовал 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)
При замене символ
&
представляет совпадающий текстsub
. Этого нужно избежать. например:value='- \\& -'