Jak używać zmiennych powłoki w skrypcie awk?

290

Znalazłem kilka sposobów przekazywania zewnętrznych zmiennych powłoki do awkskryptu, ale nie jestem pewien , czy 'i ".

Najpierw próbowałem ze skryptem powłoki:

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

Następnie spróbowałem awk:

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

Skąd ta różnica?

Wreszcie próbowałem tego:

$ 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 

Jestem tym zmieszany.

hqjma
źródło
2
Podoba mi się opcja -v, jak pokazano poniżej, ale jest to naprawdę świetne ćwiczenie w myśleniu o tym, jak chronić rzeczy przed powłoką. Pracując nad tym, moje pierwsze cięcie używało odwrotnych ukośników na spacjach i znakach dolara. Nie trzeba dodawać, że przykłady tutaj były warte mojego czasu.
Chris
Jeśli twoje wyszukiwanie awk wymaga wyrażenia regularnego , nie możesz go umieścić /var/. Zamiast tego użyj tyldy:awk -v var="$var" '$0 ~ var'
Noam Manos

Odpowiedzi:

496

Przenoszenie zmiennych powłoki do awk

można to zrobić na kilka sposobów. Niektóre są lepsze od innych. Powinno to obejmować większość z nich. Jeśli masz komentarz, zostaw poniżej. wersja 1.5


Korzystanie -v (najlepszy sposób, najbardziej przenośny)

Użyj -vopcji: (PS użyj spacji po -vlub będzie mniej przenośna. Np. awk -v var=Nie awk -vvar=)

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

Powinno to być zgodne z większością awk, a zmienna jest również dostępna w BEGINbloku:

Jeśli masz wiele zmiennych:

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

Ostrzeżenie . Jak pisze Ed Morton, sekwencje specjalne będą interpretowane, więc \tstają się rzeczywistością, taba nie, \tjeśli tego właśnie szukasz. Można rozwiązać za pomocą ENVIRON[]lub uzyskać do niego dostęp za pośrednictwemARGV[]

PS Jeśli podoba Ci się trzy pionowe paski jako separatory |||, nie można ich uciec, więc użyj-F"[|][|][|]"

Przykład pobierania danych z gospody programu / funkcji do awk(tutaj używana jest data)

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

Zmienna po bloku kodu

Tutaj otrzymujemy zmienną po awkkodzie. Będzie to działać dobrze, o ile nie potrzebujesz zmiennej w BEGINbloku:

variable="line one\nline two"
echo "input data" | awk '{print var}' var="${variable}"
or
awk '{print var}' var="${variable}" file
  • Dodawanie wielu zmiennych:

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

  • W ten sposób możemy również ustawić inny separator pól FSdla każdego pliku.

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

  • Zmienna po bloku kodu nie będzie działać dla BEGINbloku:

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


Tutaj ciąg

Zmienna może być również dodana do awkkorzystania z ciągu tutaj z powłok, które je obsługują (w tym Bash):

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

To jest to samo co:

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

PS traktuje zmienną jako plik wejściowy.


ENVIRON Wejście

Jak pisze TrueY, możesz używać ENVIRONdo drukowania zmiennych środowiskowych . Ustawiając zmienną przed uruchomieniem AWK, możesz wydrukować ją w następujący sposób:

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

ARGV Wejście

Jak pisze Steven Penny, możesz użyć, ARGVaby wprowadzić dane do awk:

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

Aby wprowadzić dane do samego kodu, nie tylko POCZĄTEK:

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

Zmienna w kodzie: UŻYWAJ Z ostrożnością

Możesz użyć zmiennej w awkkodzie, ale jest ona nieuporządkowana i trudna do odczytania, a jak Charles Duffyzauważa, ta wersja może również być ofiarą wstrzyknięcia kodu. Jeśli ktoś doda złe elementy do zmiennej, zostanie ona wykonana jako część awkkodu.

Działa to poprzez wyodrębnienie zmiennej w kodzie, dzięki czemu staje się jej częścią.

Jeśli chcesz, aby awkzmiany zmieniały się dynamicznie przy użyciu zmiennych, możesz to zrobić w ten sposób, ale NIE używaj go do zmiennych normalnych.

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

Oto przykład wstrzyknięcia kodu:

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

W awkten sposób możesz dodać wiele poleceń . Nawet powoduje awarię przy użyciu niepoprawnych poleceń.


Informacje dodatkowe:

Zastosowanie podwójnej wyceny

Zawsze dobrze jest podwoić zmienną, "$variable"
jeśli nie, wiele wierszy zostanie dodanych jako długa pojedyncza linia.

Przykład:

var="Line one
This is line two"

echo $var
Line one This is line two

echo "$var"
Line one
This is line two

Inne błędy, które możesz uzyskać bez podwójnego cytowania:

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

I przy pojedynczym cudzysłowie nie rozwija wartości zmiennej:

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

Więcej informacji o AWK i zmiennych

Przeczytaj to FAQ .

Jotne
źródło
2
„bałagan i trudny do odczytania” ignoruje ważniejsze obawy związane z bezpieczeństwem wstrzykiwania kodu przy bezpośrednim podstawianiu ciągów znaków w kodzie awk.
Charles Duffy
czytając powyższą odpowiedź, mogę uruchomić skrypt bez błędów, ale to nie działa: 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. Czy z powodu zagnieżdżonego nawiasu {?
Darion Badlydone
@DarionBadlydone Wypróbuj to awk -v repo="$1" -v tag="$2" 'BEGIN {print "repo="repo,"tag="tag}'. Zobaczy, czy wypisze zmienną. Napisz własne pytanie, jeśli nie możesz się zorientować.
Jotne
@Jotne tak, wypisuje wartości, więc spróbowałem w ten sposób: 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 ale nie działa zgodnie z oczekiwaniami. Zastępuje każdą linię pliku źródłowego wydrukowanym poleceniem
Darion Badlydone
@Jotne Zrobiłem to z sed, i tak dziękuję
Darion Badlydone
28

Wygląda na to, że stary dobry ENVIRON wbudowany skrót nie jest wcale wspomniany. Przykład jego użycia:

$ X=Solaris awk 'BEGIN{print ENVIRON["X"], ENVIRON["TERM"]}'
Solaris rxvt
Prawda
źródło
4
To dobra sugestia, ponieważ przekazuje dane dosłownie. -vnie działa, gdy wartość zawiera ukośniki odwrotne.
ten drugi facet
2
@thatotherguy Nie wiedziałem tego! Pomyślałem, że jeśli awk -v x='\c\d' ...użyję, to użyję go właściwie. Ale po xwydrukowaniu awk upuszcza słynny: awk: warning: escape sequence '\c' treated as plain 'c'komunikat o błędzie ... Dzięki!
TrueY
Działa poprawnie - poprawnie w tym kontekście oznacza rozwinięcie sekwencji ucieczki, ponieważ w taki sposób -vzaprojektowano działanie, aby można było użyć \tw zmiennej i dopasować ją na przykład dosłowną kartę w danych. Jeśli nie jest to pożądane zachowanie, to nie korzystasz, -vktórego używasz ARGV[]lub ENVIRON[].
Ed Morton
9

Użyj jednego z tych, w zależności od tego, jak chcesz obsługiwać odwrotne ukośniki w zmiennych powłoki ( avarjest to zmienna awk, svarjest zmienna powłoki):

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

Zobacz http://cfajohnson.com/shell/cus-faq-2.html#Q24, aby uzyskać szczegółowe informacje i inne opcje. Pierwsza powyższa metoda jest prawie zawsze najlepszą opcją i ma najbardziej oczywistą semantykę.

Ed Morton
źródło
6

Możesz przekazać opcję wiersza polecenia -v ze zmienną name ( v) i wartością ( =) zmiennej środowiskowej ( "${v}"):

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

Lub, aby było to jaśniejsze (przy znacznie mniejszej liczbie vs):

% environment_variable=123test
% awk -vawk_variable="${environment_variable}" 'BEGIN { print awk_variable }'
123test
Johnsyweb
źródło
3

Możesz skorzystać z ARGV:

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

Zauważ, że jeśli zamierzasz kontynuować pracę w ciele, musisz dostosować ARGC:

awk 'BEGIN {ARGC--} {print ARGV[2], $0}' file "$v"
Steven Penny
źródło
1

Właśnie zmieniłem odpowiedź @ Jotne na „for loop”.

for i in `seq 11 20`; do host myserver-$i | awk -v i="$i" '{print "myserver-"i" " $4}'; done
edib
źródło
1
To tylko zdaje się być kolejną ilustracją tego, jak korzystać z -vopcji Awk, która była już wspomniana w wielu istniejących odpowiedziach. Jeśli chcesz pokazać, jak uruchomić Awk w pętli, to naprawdę jest inne pytanie.
tripleee
0

Musiałem wstawić datę na początku wierszy pliku dziennika i zrobiłem to tak, jak poniżej:

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

Można go przekierować do innego pliku w celu zapisania

Sina
źródło
Podwójny cytat - pojedynczy cytat - podwójny cytat był dokładnie tym, czego potrzebowałem, aby mój działał.
user53029,
2
Zostało to już wspomniane w zaakceptowanej odpowiedzi jako metoda, której nie należy używać z powodu luk w zabezpieczeniach związanych z wstrzykiwaniem kodu. Informacje tutaj są więc zbędne (już opisane w zaakceptowanej odpowiedzi) i niekompletne (nie wspomina o problemach z tą metodą).
Jason S