Як я можу використовувати змінні оболонки в 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 

Мене з цим бентежить.


2
Мені подобається -v, як показано нижче, але це справді чудова вправа в роздумах про те, як захистити речі від оболонки. Проробляючи це, в моєму першому розрізі використовуються нахили на пробіли та знаки долара. Потрібно сказати, що приклади тут гідні мого часу.
Кріс


Якщо ваш пошук awk потребує регулярного вираження , ви не можете його ставити /var/. Замість цього використовуйте tilde:awk -v var="$var" '$0 ~ var'
Noam Manos

Відповіді:


496

Введення змінних оболонок у 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}'

Попередження . Як пише Ед Мортон, послідовності втечі будуть інтерпретуватися так, що \tстає реальністю, tabа не, \tякщо саме це ви шукаєте. Можна вирішити за допомогою ENVIRON[]або отримати доступ до нього черезARGV[]

PS Якщо вам подобається три вертикальні смуги в якості роздільника |||, його неможливо уникнути, тому використовуйте-F"[|][|][|]"

Приклад отримання даних із програми / функції заїзду до 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для отримання даних у див:

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 та змінні

Прочитайте цей текст .


2
"безладно і важко читати" ігнорує важливішу проблему безпеки введення коду при безпосередній підміні рядків у код awk.
Чарльз Даффі

читаючи відповідь вище, я можу запустити свій скрипт без помилок, але він не справляється з цим: awk -v repo = "$ 1" -v tag = "$ 2" '{sub (/ image: registerabx.azurecr.io \ / { print repo}: ([a-z0-9] +) $ /, "image: registerabc.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

@Jotne так, він надрукує значення, тому я спробував таким чином: awk -v repo = "$ 1" -v tag = "$ 2" '{print "{sub (/ image: registerabc.azurecr.io/"repo" :( [a-z0-9] +) $ /, \ "image: registerabc.azurecr.io/"repo":"tag"\");}1"} "./services/appscompose.yaml >> newcompose.yaml але не працює як аспект. Він замінює кожен рядок вихідного файлу друкованою командою
Darion Badlydone

@Jotne Я зробив це з sed, Дякую все одно
Darion Badlydone

28

Здається, що добрий-старий ENVIRON вбудований хеш взагалі не згадується. Приклад його використання:

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

4
Це гарна пропозиція, оскільки вона передає дані дослівно. -vне працює, коли значення містить зворотні риски.
той інший хлопець

2
@thatotherguy Я цього не знав! Я думав, що якщо я буду користуватися, awk -v x='\c\d' ...то він буде використовуватись належним чином. Але коли xдрукується awk, випадає відоме: awk: warning: escape sequence '\c' treated as plain 'c'повідомлення про помилку ... Дякую!
TrueY

Це працює належним чином - належним чином у цьому контексті означає розширення послідовностей евакуації, оскільки саме так -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 зі змінною ім'ям ( v) та значенням ( =) змінної середовища ( "${v}"):

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

Або зробити це зрозумілішим (із значно меншою кількістю vs):

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

3

Ви можете використовувати ARGV:

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

Зауважте, що якщо ви збираєтеся продовжувати працювати в тілі, вам потрібно буде відрегулювати ARGC:

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

1

Я щойно змінив відповідь @ Jotne на "for loop".

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

1
Це просто здається ще однією ілюстрацією того, як використовувати -vваріант Awk, який уже згадувався у багатьох існуючих відповідях. Якщо ви хочете показати, як запустити Awk у циклі, це справді інше питання.
tripleee

0

Мені довелося вставити дату на початку рядків файлу журналу, і це робиться як нижче:

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

Для збереження його можна перенаправити на інший файл


Подвійна цитата - одинарна цитата - подвійна цитата була саме тим, що мені потрібно було зробити для моєї роботи.
user53029

2
Про це вже згадувалося у прийнятій відповіді як метод, який ви не повинні використовувати через уразливості введення коду. Тож інформація тут є зайвою (вже описана у прийнятій відповіді) та неповною (не згадує проблем із цим методом).
Джейсон S
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.