#awk #escaping #gsub
#awk #экранирование #gsub
Вопрос:
Рассмотрим следующие входные данные:
$ cat a
d:
$ cat a.awk
{ sub("\", "\\"); print $0 }
$ cat a_double.awk
{ sub("\\", "\\"); print $0 }
Теперь запуск cat a | awk -f a.awk
дает
d:
и запуск cat a | awk -f a_double.awk
дает
d:\
и я ожидаю ровно наоборот. Как я должен это интерпретировать?
$ awk -V
GNU Awk 4.1.4, API: 1.1 (GNU MPFR 4.0.1, GNU MP 6.1.2)
Ответ №1:
Да, его ожидаемое поведение awk
. Когда вы запускаете sub("\", "\\")
свой первый скрипт, внутри sub
"
(двойные кавычки), поскольку мы НЕ используем /
для сопоставления шаблон, нам нужно сначала экранировать
(фактический буквальный символ), затем для экранирования, которое мы используем
, поэтому нам также нужно экранировать это, следовательно, это станет \\
\ \
| |
| |
first 2 chars are denoting escaping next 2 chars are denoting actual literal character
Чего НЕ происходит в вашем 1-м случае, следовательно, НЕТ совпадения, поэтому в нем нет замены, в вашем 2-м скрипте awk вы делаете это (экранирующая часть в разделе сопоставления регулярных sub
выражений), следовательно, она
идеально соответствует.
Давайте посмотрим на это на примере и попробуем поставить ...
для целей проверки.
Когда ничего не происходит: поскольку нет совпадения на
awk '{sub("\", "....\\"); print $0}' Input_file
d:
Когда происходит сопоставление шаблонов:
awk '{sub("\\", "...\\"); print $0}' Input_file
d:...\
От man awk
:
gsub(r, s [, t])
For each substring matching the regular expression r in the string t,
substitute the string s, and return the number of substitutions.
Как мы могли бы выполнить фактическую экранирующую часть (где нам нужно использовать только
перед символом только один раз)? Упомяните ваше регулярное /../
выражение в первом разделе sub
like, и нам НЕ нужно
здесь дважды экранировать.
awk '{sub(/\/,"amp;\")} 1' Input_file
Ответ №2:
Первым аргументом *sub()
является регулярное выражение, а не строка, поэтому вы должны использовать разделители regexp ( /.../
), а не string ( "..."
) . Первое — это буквальное регулярное выражение, которое используется как есть, в то время как второе определяет динамическое (или вычисляемое) регулярное выражение, которое заставляет awk интерпретировать строку дважды: первый раз для преобразования строки в регулярное выражение, а второй — для использования ее в качестве регулярного выражения, следовательно, удваивает обратную косую черту, необходимую для экранирования. См. https://www.gnu.org/software/gawk/manual/gawk.html#Computed-Regexps .
В следующем нам просто нужно один раз экранировать обратную косую черту, потому что мы используем буквальное, а не динамическое регулярное выражение:
$ cat a
d:
$ awk '{sub(/\/,"\\")}1' a
d:\
Ваш первый скрипт по праву выдал бы синтаксическую ошибку в более поздней версии gawk (5.1.0), поскольку "\"
в динамическом регулярном выражении эквивалентно //
в буквальном, и в этом выражении обратная косая черта экранирует последнюю косую черту, что означает отсутствие конечного разделителя:
$ cat a.awk
{ sub("\", "\\"); print $0 }
$ awk -f a.awk a
awk: a.awk:1: (FILENAME=a FNR=1) fatal: invalid regexp: Trailing backslash: //