Как использовать переменные оболочки в скрипте awk?

290

Я нашел несколько способов передать внешние переменные оболочки в awkсценарий, но я запутался в 'и ".

Сначала я попытался с помощью сценария оболочки:

$ v=123test
$ echo $v
123test
$ echo "$v"
123test

Тогда попробовал awk:

$ awk 'BEGIN{print "'$v'"}'
$ 123test
$ awk 'BEGIN{print '"$v"'}'
$ 123

Почему разница?

Наконец я попробовал это:

$ awk 'BEGIN{print " '$v' "}'
$  123test
$ awk 'BEGIN{print ' "$v" '}'
awk: cmd. line:1: BEGIN{print
awk: cmd. line:1:             ^ unexpected newline or end of string 

Я запутался в этом.

hqjma
источник
2
Мне нравится -v, как показано ниже, но это действительно отличное упражнение для размышления о том, как защитить вещи от оболочки. Работая с этим, я впервые использовал обратную косую черту на пробелах и знаках доллара. Излишне говорить, что примеры здесь стоили моего времени.
Крис
Если ваш поиск в awk требует регулярного выражения , вы не можете поставить /var/. Вместо этого используйте тильду:awk -v var="$var" '$0 ~ var'
Ноам Манос

Ответы:

497

Получение переменных оболочки в awk

может быть сделано несколькими способами. Некоторые лучше, чем другие. Это должно охватывать большинство из них. Если у вас есть комментарий, пожалуйста, оставьте ниже. v1.5


Использование -v (Лучший способ, самый портативный)

Используйте -vопцию: (PS используйте пробел после -vили он будет менее переносимым. Например, awk -v var=нет awk -vvar=)

variable="line one\nline two"
awk -v var="$variable" 'BEGIN {print var}'
line one
line two

Это должно быть совместимо с большинством awk, и переменная также доступна в BEGINблоке:

Если у вас есть несколько переменных:

awk -v a="$var1" -v b="$var2" 'BEGIN {print a,b}'

Предупреждение . Как пишет Эд Мортон, escape-последовательности будут интерпретироваться, поэтому они \tстановятся реальными, tabа не \tесли это то, что вы ищете. Может быть решен с помощью ENVIRON[]или доступ к нему черезARGV[]

PS Если вам нравятся три вертикальные черты в качестве разделителя |||, от них нельзя убежать, поэтому используйте-F"[|][|][|]"

Пример получения данных из программы / функции inn в awk(здесь используется дата)

awk -v time="$(date +"%F %H:%M" -d '-1 minute')" 'BEGIN {print time}'

Переменная после блока кода

Здесь мы получаем переменную после awkкода. Это будет работать нормально, если вам не нужна переменная в BEGINблоке:

variable="line one\nline two"
echo "input data" | awk '{print var}' var="${variable}"
or
awk '{print var}' var="${variable}" file
  • Добавление нескольких переменных:

awk '{print a,b,$0}' a="$var1" b="$var2" file

  • Таким образом, мы также можем установить разные разделители полей FSдля каждого файла.

awk 'some code' FS=',' file1.txt FS=';' file2.ext

  • Переменная после блока кода не будет работать для BEGINблока:

echo "input data" | awk 'BEGIN {print var}' var="${variable}"


Здесь строка

Переменная также может быть добавлена ​​к awkиспользованию здесь-строки из поддерживаемых ими оболочек (включая Bash):

awk '{print $0}' <<< "$variable"
test

Это так же, как:

printf '%s' "$variable" | awk '{print $0}'

PS это обрабатывает переменную как файл ввода.


ENVIRON вход

Как пишет TrueY, вы можете использовать ENVIRONдля печати переменных среды . Задав переменную перед запуском AWK, вы можете распечатать ее следующим образом:

X=MyVar
awk 'BEGIN{print ENVIRON["X"],ENVIRON["SHELL"]}'
MyVar /bin/bash

ARGV вход

Как пишет Стивен Пенни, вы можете использовать ARGVданные в awk:

v="my data"
awk 'BEGIN {print ARGV[1]}' "$v"
my data

Чтобы получить данные в самом коде, а не только НАЧАТЬ:

v="my data"
echo "test" | awk 'BEGIN{var=ARGV[1];ARGV[1]=""} {print var, $0}' "$v"
my data test

Переменная в коде: использовать с осторожностью

Вы можете использовать переменную в awkкоде, но она грязная и трудная для чтения, и, как Charles Duffyотмечается, эта версия также может стать жертвой внедрения кода. Если кто-то добавляет в переменную плохой материал, он будет выполнен как часть awkкода.

Это работает путем извлечения переменной в коде, поэтому она становится ее частью.

Если вы хотите создать awkдинамическое изменение с использованием переменных, вы можете сделать это таким образом, но НЕ используйте его для обычных переменных.

variable="line one\nline two"
awk 'BEGIN {print "'"$variable"'"}'
line one
line two

Вот пример внедрения кода:

variable='line one\nline two" ; for (i=1;i<=1000;++i) print i"'
awk 'BEGIN {print "'"$variable"'"}'
line one
line two
1
2
3
.
.
1000

Вы можете добавить много команд awkтаким образом. Даже сбой с недействительными командами.


Дополнительная информация:

Использование двойной кавычки

Всегда полезно "$variable"
заключить в кавычки переменную. Если нет, несколько строк будут добавлены в виде одной длинной строки.

Пример:

var="Line one
This is line two"

echo $var
Line one This is line two

echo "$var"
Line one
This is line two

Другие ошибки, которые вы можете получить без двойной кавычки:

variable="line one\nline two"
awk -v var=$variable 'BEGIN {print var}'
awk: cmd. line:1: one\nline
awk: cmd. line:1:    ^ backslash not last character on line
awk: cmd. line:1: one\nline
awk: cmd. line:1:    ^ syntax error

И с одинарной кавычкой, это не расширяет значение переменной:

awk -v var='$variable' 'BEGIN {print var}'
$variable

Больше информации о AWK и переменных

Прочитайте этот FAQ .

Jotne
источник
2
«Грязный и трудно читаемый» игнорирует более важную проблему безопасности при внедрении кода при прямой подстановке строк в код awk.
Чарльз Даффи
прочитав ответ выше, я могу запустить свой сценарий без ошибок, но он не выполняет свою работу: awk -v repo = "$ 1" -v tag = "$ 2" '{sub (/ image: registryabx.azurecr.io \ / { print repo}: ([a-z0-9] +) $ /, "image: registryabc.azurecr. io / {print repo}: {print tag}");} 1 './services/appscompose.yaml >> newcompose.yaml. Это из-за вложенных скобок {?
Дарион Бадлидоне
@DarionBadlydone Попробуйте это awk -v repo="$1" -v tag="$2" 'BEGIN {print "repo="repo,"tag="tag}'. Он увидит, печатает ли она переменную. Оставьте свой вопрос, если не можете разобраться.
Йотне
@Jotne да, он печатает значения, поэтому я попытался следующим образом: awk -v repo = "$ 1" -v tag = "$ 2" '{print "{sub (/ image: registryabc.azurecr.io/"repo" :( [a-z0-9] +) $ /, \ "image: registryabc.azurecr.io/"repo":"tag"\"); enj1"} './services/appscompose.yaml >> newcompose.yaml но не работает так, как это требуется. Он заменяет каждую строку исходного файла напечатанной командой
Дарион Бадлидон
@Jotne Я сделал это с помощью Sed, спасибо, в любом случае
Дарион Бадлидон
28

Кажется, что старый добрый ENVIRON встроенный хеш вообще не упоминается. Пример его использования:

$ X=Solaris awk 'BEGIN{print ENVIRON["X"], ENVIRON["TERM"]}'
Solaris rxvt
TrueY
источник
4
Это хорошее предложение, потому что оно передает данные дословно. -vне работает, если значение содержит обратную косую черту.
тот другой парень
2
@thatotherguy Я этого не знал! Я думал, что если я буду использовать awk -v x='\c\d' ...то он будет использоваться правильно. Но когда xпечатается, awk выдает известное awk: warning: escape sequence '\c' treated as plain 'c'сообщение об ошибке ... Спасибо!
Правда 25
Он работает должным образом - в данном контексте правильно означает расширение escape-последовательностей, потому что именно так он и -vбыл разработан, чтобы вы могли использовать \tпеременную и сопоставить ее, например, с литеральной вкладкой в ​​данных. Если это не то поведение, которое вы хотите, то вы не используете, -vвы используете ARGV[]или ENVIRON[].
Эд Мортон,
9

Используйте любой из них в зависимости от того, как вы хотите, чтобы в переменных оболочки обрабатывались обратные слеши ( avarэто переменная awk, svarэто переменная оболочки):

awk -v avar="$svar" '... avar ...' file
awk 'BEGIN{avar=ARGV[1];ARGV[1]=""}... avar ...' "$svar" file

Смотрите http://cfajohnson.com/shell/cus-faq-2.html#Q24 для деталей и других опций. Первый метод, описанный выше, почти всегда является вашим лучшим вариантом и имеет наиболее очевидную семантику.

Эд Мортон
источник
6

Вы можете передать параметр командной строки -v с помощью переменной name ( v) и значения ( =) переменной среды ( "${v}"):

% awk -vv="${v}" 'BEGIN { print v }'
123test

Или чтобы было понятнее (с гораздо меньшим количеством vс):

% environment_variable=123test
% awk -vawk_variable="${environment_variable}" 'BEGIN { print awk_variable }'
123test
Johnsyweb
источник
3

Вы можете использовать ARGV:

v=123test
awk 'BEGIN {print ARGV[1]}' "$v"

Обратите внимание, что если вы собираетесь продолжить в теле, вам нужно настроить ARGC:

awk 'BEGIN {ARGC--} {print ARGV[2], $0}' file "$v"
Стивен Пенни
источник
1

Я только что изменил ответ @ Jotne на "для цикла".

for i in `seq 11 20`; do host myserver-$i | awk -v i="$i" '{print "myserver-"i" " $4}'; done
edib
источник
1
Это просто кажется еще одной иллюстрацией того, как использовать -vопцию Awk, которая уже упоминалась во многих существующих ответах. Если вы хотите показать, как запустить Awk в цикле, это действительно другой вопрос.
tripleee
0

Мне пришлось вставить дату в начале строки файла журнала, и это сделано, как показано ниже:

DATE=$(date +"%Y-%m-%d")
awk '{ print "'"$DATE"'", $0; }' /path_to_log_file/log_file.log

Это можно перенаправить в другой файл, чтобы сохранить

Сина
источник
Двойная кавычка - одинарная кавычка - двойная кавычка была именно тем, что мне нужно, чтобы моя работа работала.
user53029
2
Это уже упоминалось в принятом ответе как метод, который вы не должны использовать из-за уязвимостей внедрения кода. Таким образом, информация здесь является избыточной (уже описанной в принятом ответе) и неполной (не затрагивает проблемы с этим методом).
Джейсон С