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

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

Сначала я попробовал с оболочкой script:

$ 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 

Я смущен этим.

Ответ 1

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

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


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

Используйте опцию -v: (P.S. используйте пробел после -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

Использование переменной таким способом не работает в блоке BEGIN:

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

Здесь строка

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

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

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

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

P.S. это рассматривает переменную как входной файл.


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.

Ответ 2

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

$ X=Solaris awk 'BEGIN{print ENVIRON["X"], ENVIRON["TERM"]}'
Solaris rxvt

Ответ 3

Использовать любой из них в зависимости от того, как вы хотите обратную косую черту в обрабатываемых переменных оболочки (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. Первый метод, приведенный выше, почти всегда является вашим лучшим вариантом и имеет самую очевидную семантику.

Ответ 4

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

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

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

% environment_variable=123test
% awk -vawk_variable="${environment_variable}" 'BEGIN { print awk_variable }'
123test

Ответ 5

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

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

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

awk 'BEGIN {ARGC--} {print ARGV[2], $0}' file "$v"

Ответ 6

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

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

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

Ответ 7

for i in chr{1..22} chrX chrY
do
awk -v chr="$i" '$1==chr' ../snp150.hg19.txt >> $chr.vcf.bed
echo $i
done

Ответ 8

Я просто изменил @Jotne ответ для "for loop".

for i in 'seq 11 20'; do host myserver-$i | awk -v i="$i" '{print "myserver-"i" " $4}'; done