Использовать переменную оболочки в awk

Вопрос или проблема

Вот мой скрипт (для поиска файлов, содержащих заданный шаблон):

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.

Применение

  1. Создание переменной awk из переменной оболочки:

    Для того чтобы связать переменную оболочки с awk, используется конструкция -v. Это работает для всех значений, включая строки и числа, и позволяет нам использовать данную переменную внутри обработки текста awk.

  2. Работа с условными выражениями:

    Далее в awk применяются условия. Например, $0 ~ vawk проверяет, содержит ли текущая строка файл совпадения с vawk. Это мощное средство фильтрации информации.

  3. Конструкция завершения обработки:

    Блок END используется для обработки данных после завершения цикла. Если c (счетчик совпадений) равен нулю, команда завершит выполнение с кодом 1, сигнализируя, что совпадений не найдено.

Разные подходы

В Unix-системах переменные можно передавать в awk несколькими способами:

  1. Через флаг -v:

    Это самый частый и безопасный способ передачи значений переменной. Он исключает влияние среды на интерпретацию данных внутри awk.

  2. Через массив ENVIRON:

    В некоторых случаях можно использовать ENVIRON для чтения переменных окружения:

    x="value"
    awk '$2 == ENVIRON["x"] {print $1}' infile
  3. Использование аргументов:

    Можно также использовать массив ARGV, чтобы избежать влияния на обработку файла переменных среды:

    awk -v var="$1" 'BEGIN {for (i=1; i<ARGC; i++) if (ARGV[i] == var) print "Found", var; exit}'

Выбор подхода зависит от конкретной задачи и окружения. Например, если переменная может содержать символы, влияющие на интерпретацию (такие как обратные слеши или специальные символы), рекомендуется использование ENVIRON или корректное экранирование.

Заключение

Использование переменных оболочки в awk важно для автоматизации задач и улучшения взаимодействия скриптов с пользователем. Правильное понимание передачи переменных позволяет избежать распространённых ошибок, таких как некорректное определение переменных внутри awk или ошибки в работе с ними в зависимости от форматов данных. Изучение этих техник и их правильное применение принесут большую пользу при написании сложных скриптов и решении оперативных задач в Unix-системах.

Оцените материал
Добавить комментарий

Капча загружается...