Вопрос или проблема
Вот мой скрипт (для поиска файлов, содержащих заданный шаблон):
find . -type f \
-exec awk -v vawk="$1" "https://unix.stackexchange.com/""$vawk"'/ {c++} c>0 { print ARGV[1]; exit 0 } END { if (! c) {exit 1}}' \{\} \;
Я хотел бы использовать свой скрипт с аргументом §:
MyScript.sh pattern
Моя проблема в том, что я не могу передать переменную $1
в awk
.
Когда я пытаюсь отладить свой скрипт
bash -x MyScript.sh pattern
Вот вывод:
+ find . -type f -exec awk -v vawk=pattern '// {c++} c>0 {print ARGV[1] ; exit 0 } END { if (! c) {exit 1}}' '{}' ';'
Переменная $vawk
кажется пустой.
Какие-нибудь идеи?
Похоже, вы путаете переменные awk и переменные shell. awk -v vawk="$1"
создает переменную awk с именем vawk
, но вы пытаетесь использовать синтаксис shell ($vawk
). Это не работает, потому что в shell нет переменной vawk
. Мне кажется, вы хотели:
awk -v vawk="$1" '$0 ~ vawk { c++ } # ...'
# ^ синтаксис переменной awk
Воспроизведено из этого вопроса, теперь закрытого как дубликат, поскольку он включает в себя предупреждения о ограничениях передачи переменных awk, которые могут быть полезны.
Переменная shell — это всего лишь: переменная shell. Если вы хотите превратить её в переменную awk, вам нужен синтаксис вроде:
awk -v x="$x" '$2 == x {print $1}' infile
или
awk '$2 == x {print $1}' x="$x" infile
Однако у этих подходов есть проблема: escape-последовательности в них интерпретируются.
Также, в GNU awk
версии 4.2 или выше, если $x
начинается с @/
и заканчивается на /
, она рассматривается как регулярное выражение).
Таким образом, если переменная shell содержит два символа обратную косую черту и n, переменная awk в итоге будет содержать символ новой строки, а с gawk 4.2+, если она содержит @/foo/
, переменная awk будет содержать foo
и будет типа regexp
. Хуже того, если это @/(xxxxx){1,20000}/
, например, gawk потребует один CPU на часы или до исчерпания памяти, пытаясь скомпилировать это регулярное выражение, создавая своего рода уязвимость для DoS.
Другой подход (но, как и для -v
, требующий POSIX awk или nawk (в отличие от awk 1970-х годов, все еще находящегося как /bin/awk
в Solaris)) – использовать переменные окружения:
x="$x" awk '$2 == ENVIRON["x"] {print $1}' infile
Другой подход (также с новыми awk) — использовать массив ARGV в awk:
awk -- 'BEGIN {x = ARGV[1]; delete ARGV[1]}
$2 == x {print $1}' "$x" infile
Также имейте в виду, что если вы используете ARGV
/ENVIRON
/-v
или аргументы вида var=value
, соответствующая строка будет рассматриваться как числовая строка, если её форма похожа на число (с диапазоном распознаваемых форматов чисел, различающихся в зависимости от реализации).
Это важно, потому что в случае $2 == ENVIRON["VAR"]
выше, это будет строковое сравнение¹, если $VAR
, например, foo
или 1f2
, но числовое сравнение, если это 1e2
или 1.1
(или возможно inf
, 0xff
в зависимости от реализации и версии awk
), предполагая, что $2
также выглядит числовым. Таким образом, 10.0e1
, 100
и 1e2
будут рассматриваться как равные.
Выполнение:
awk 'BEGIN {var = "" ENVIRON["VAR"]}'
Убедится, что переменная var
awk
всегда будет трактоваться как строка, даже если переменная $VAR
shell выглядит как число.
awk 'BEGIN {var = 0 + ENVIRON["VAR"]}'
Преобразует её в число (по крайней мере, началную часть, которая может быть интерпретирована как число).
¹ или сравнение strcoll()
с некоторыми реализациями (как требовалось раньше по POSIX), то есть, a == b
, где либо a
, либо b
, либо оба являются строкой, возвратит true, если a
и b
имеют одинаковый порядок сортировки.
Например, чтобы вычислить результат с плавающей точкой, можно вставить значение из внешней переменной напрямую, вот так:
f="3.75"
awk 'BEGIN { printf '"$f"' - 1.5 ; }'
Ответ или решение
Использование переменной оболочки в awk
является распространённой задачей при написании скриптов в Unix-подобных системах. Вопрос касается корректного переноса значения переменной оболочки в переменную внутри awk
. Рассмотрим теорию, пример и практическое применение данного вопроса, чтобы подробно ответить на вопрос.
Теория
awk
— это мощный инструмент для обработки текстовых файлов и вывода. Он работает с текстовыми строками, разбивая их на поля и применяя различные условные операторы и функции. Иногда возникает необходимость передать значение переменной из оболочки в awk
. Это может быть необходимо при фильтрации данных или выполнении специфических операций на основе внешних условий.
Проблема
Автор хочет передать шаблон поиска из строки аргументов командной строки в команду awk
, однако сталкивается с проблемой: переменная, объявленная в оболочке, не используется корректно в команде awk
. При попытке запускать скрипт происходит ошибка, потому что переменная vawk
пустая либо неправильно интерпретируется.
Пример
Для корректного использования переменной оболочки внутри awk
необходимо передать её с помощью флага -v
, позволяющего считывать значения из оболочки. Это делается так:
awk -v vawk="$1" '$0 ~ vawk { c++ } c > 0 { print ARGV[1]; exit 0 } END { if (!c) {exit 1}}'
Здесь vawk
— это переменная awk
, которая принимает значение из переменной оболочки $1
.
Применение
-
Создание переменной
awk
из переменной оболочки:Для того чтобы связать переменную оболочки с
awk
, используется конструкция-v
. Это работает для всех значений, включая строки и числа, и позволяет нам использовать данную переменную внутри обработки текстаawk
. -
Работа с условными выражениями:
Далее в
awk
применяются условия. Например,$0 ~ vawk
проверяет, содержит ли текущая строка файл совпадения сvawk
. Это мощное средство фильтрации информации. -
Конструкция завершения обработки:
Блок
END
используется для обработки данных после завершения цикла. Еслиc
(счетчик совпадений) равен нулю, команда завершит выполнение с кодом 1, сигнализируя, что совпадений не найдено.
Разные подходы
В Unix-системах переменные можно передавать в awk
несколькими способами:
-
Через флаг
-v
:Это самый частый и безопасный способ передачи значений переменной. Он исключает влияние среды на интерпретацию данных внутри
awk
. -
Через массив
ENVIRON
:В некоторых случаях можно использовать
ENVIRON
для чтения переменных окружения:x="value" awk '$2 == ENVIRON["x"] {print $1}' infile
-
Использование аргументов:
Можно также использовать массив
ARGV
, чтобы избежать влияния на обработку файла переменных среды:awk -v var="$1" 'BEGIN {for (i=1; i<ARGC; i++) if (ARGV[i] == var) print "Found", var; exit}'
Выбор подхода зависит от конкретной задачи и окружения. Например, если переменная может содержать символы, влияющие на интерпретацию (такие как обратные слеши или специальные символы), рекомендуется использование ENVIRON
или корректное экранирование.
Заключение
Использование переменных оболочки в awk
важно для автоматизации задач и улучшения взаимодействия скриптов с пользователем. Правильное понимание передачи переменных позволяет избежать распространённых ошибок, таких как некорректное определение переменных внутри awk
или ошибки в работе с ними в зависимости от форматов данных. Изучение этих техник и их правильное применение принесут большую пользу при написании сложных скриптов и решении оперативных задач в Unix-системах.