Wie verwende ich Shell-Variablen in einem awk-Skript?

289

Ich habe einige Möglichkeiten gefunden, externe Shell-Variablen an ein awkSkript zu übergeben, bin aber verwirrt über 'und ".

Zuerst habe ich es mit einem Shell-Skript versucht:

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

Dann versuchte awk:

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

Warum ist der Unterschied?

Zuletzt habe ich das versucht:

$ 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 

Ich bin verwirrt darüber.

hqjma
quelle
2
Ich mag das -v wie unten gezeigt, aber dies ist wirklich eine großartige Übung, um darüber nachzudenken, wie man Dinge vor der Hülle schützt. Bei meinem ersten Schnitt werden Backslashes auf Leerzeichen und Dollarzeichen verwendet. Unnötig zu erwähnen, dass die Beispiele hier meine Zeit wert waren.
Chris
Wenn Ihre awk-Suche einen regulären Ausdruck benötigt , können Sie dies nicht tun /var/. Verwenden Sie stattdessen Tilde:awk -v var="$var" '$0 ~ var'
Noam Manos

Antworten:

496

Shell-Variablen in awk

kann auf verschiedene Arten erfolgen. Einige sind besser als andere. Dies sollte die meisten von ihnen abdecken. Wenn Sie einen Kommentar haben, hinterlassen Sie bitte unten. v1.5


Verwenden -v (Der beste Weg, am tragbarsten)

Verwenden Sie die -vOption: (PS verwenden ein Leerzeichen nach -voder es wird weniger portabel sein. ZB awk -v var=nicht awk -vvar=)

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

Dies sollte mit den meisten kompatibel sein awk, und die Variable ist auch im BEGINBlock verfügbar :

Wenn Sie mehrere Variablen haben:

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

Warnung . Wie Ed Morton schreibt, werden Escape-Sequenzen so interpretiert, dass sie \treal werden tabund nicht, \twenn Sie danach suchen. Kann durch Verwendung ENVIRON[]oder Zugriff über gelöst werdenARGV[]

PS Wenn Sie drei vertikale Balken als Trennzeichen mögen |||, können diese nicht maskiert werden. Verwenden Sie sie daher-F"[|][|][|]"

Beispiel zum Abrufen von Daten von einem Programm- / Funktions-Gasthaus an awk(hier wird das Datum verwendet)

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

Variable nach Codeblock

Hier erhalten wir die Variable nach dem awkCode. Dies funktioniert einwandfrei, solange Sie die Variable im BEGINBlock nicht benötigen :

variable="line one\nline two"
echo "input data" | awk '{print var}' var="${variable}"
or
awk '{print var}' var="${variable}" file
  • Hinzufügen mehrerer Variablen:

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

  • Auf diese Weise können wir auch FSfür jede Datei ein anderes Feldtrennzeichen festlegen .

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

  • Variable nach dem Codeblock funktioniert nicht für den BEGINBlock:

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


Here-String

Eine Variable kann auch zur awkVerwendung einer Here-Zeichenfolge aus Shells hinzugefügt werden , die sie unterstützen (einschließlich Bash):

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

Dies ist das gleiche wie:

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

PS Dies behandelt die Variable als Dateieingabe.


ENVIRON Eingang

Während TrueY schreibt, können Sie ENVIRONmit Umgebungsvariablen drucken . Wenn Sie eine Variable festlegen, bevor Sie AWK ausführen, können Sie sie wie folgt ausdrucken:

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

ARGV Eingang

Wie Steven Penny schreibt, können Sie verwenden ARGV, um die Daten in awk zu bringen:

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

Um die Daten in den Code selbst zu übertragen, nicht nur den BEGIN:

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

Variable innerhalb des Codes: USE WITH CAUTION

Sie können eine Variable im awkCode verwenden, diese ist jedoch unübersichtlich und schwer zu lesen. Wie bereits erwähnt Charles Duffy, ist diese Version möglicherweise auch Opfer einer Code-Injektion. Wenn jemand der Variablen schlechtes Material hinzufügt, wird es als Teil des awkCodes ausgeführt.

Dies funktioniert durch Extrahieren der Variablen innerhalb des Codes, sodass sie ein Teil davon wird.

Wenn Sie eine awkerstellen möchten, die sich bei Verwendung von Variablen dynamisch ändert, können Sie dies auf diese Weise tun, aber NICHT für normale Variablen verwenden.

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

Hier ist ein Beispiel für die Code-Injection:

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

Sie können auf awkdiese Weise viele Befehle hinzufügen . Lassen Sie es sogar mit ungültigen Befehlen abstürzen.


Zusatzinformation:

Verwendung von doppelten Anführungszeichen

Es ist immer gut, die Variable in doppelte Anführungszeichen zu setzen. "$variable"
Wenn nicht, werden mehrere Zeilen als lange einzelne Zeile hinzugefügt.

Beispiel:

var="Line one
This is line two"

echo $var
Line one This is line two

echo "$var"
Line one
This is line two

Andere Fehler, die Sie ohne doppeltes Anführungszeichen erhalten können:

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

Bei einfachen Anführungszeichen wird der Wert der Variablen nicht erweitert:

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

Weitere Informationen zu AWK und Variablen

Lesen Sie diese FAQ .

Jotne
quelle
2
"chaotisch und schwer zu lesen" ignoriert die wichtigeren Sicherheitsbedenken der Code-Injektion, wenn Zeichenfolgen direkt in awk-Code eingesetzt werden.
Charles Duffy
Wenn ich die Antwort oben lese, kann ich mein Skript fehlerfrei ausführen, aber es funktioniert nicht: 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. Liegt an der verschachtelten Klammer {?
Darion Badlydone
@DarionBadlydone Versuchen Sie dies awk -v repo="$1" -v tag="$2" 'BEGIN {print "repo="repo,"tag="tag}'. Es wird angezeigt, ob die Variable gedruckt wird. Stellen Sie eine eigene Frage, wenn Sie das nicht herausfinden können.
Jotne
@Jotne ja, es druckt die Werte, also habe ich es auf folgende Weise versucht: awk -v repo = "$ 1" -v tag = "$ 2" '{print "{sub (/ image: registryabc.azurecr.io/"repo" :( [a-z0-9] +) $ /, \ "image: registryabc.azurecr.io/"repo":"tag"\");}1"} './services/appscompose.yaml >> newcompose.yaml funktioniert aber nicht wie vorgesehen. Es ersetzt jede Zeile der Quelldatei durch den gedruckten Befehl
Darion Badlydone
@Jotne Ich habe es mit sed gemacht, trotzdem danke
Darion Badlydone
28

Es scheint, dass der gute Alte ENVIRON eingebauter Hash wird überhaupt nicht erwähnt. Ein Beispiel für seine Verwendung:

$ X=Solaris awk 'BEGIN{print ENVIRON["X"], ENVIRON["TERM"]}'
Solaris rxvt
TrueY
quelle
4
Dies ist ein guter Vorschlag, da die Daten wörtlich übergeben werden. -vfunktioniert nicht, wenn der Wert Backslashes enthält.
andere Typ
2
@thatotherguy das wusste ich nicht! Ich dachte, wenn ich es benutze awk -v x='\c\d' ..., wird es richtig benutzt. Aber wenn xgedruckt wird, lässt awk die berühmte awk: warning: escape sequence '\c' treated as plain 'c'Fehlermeldung fallen ... Danke!
TrueY
Es funktioniert ordnungsgemäß - ordnungsgemäß bedeutet in diesem Zusammenhang, Escape-Sequenzen zu erweitern, da dies -vso konzipiert wurde, dass Sie es \tin der Variablen verwenden und es beispielsweise mit einer Literal-Registerkarte in den Daten abgleichen können. Wenn dies nicht das gewünschte Verhalten ist, verwenden -vSie ARGV[]oder nicht ENVIRON[].
Ed Morton
9

Verwenden Sie avareine dieser Optionen, je nachdem, wie Backslashes in den Shell-Variablen behandelt werden sollen ( ist eine awk-Variable, svarist eine Shell-Variable):

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

Weitere Informationen und andere Optionen finden Sie unter http://cfajohnson.com/shell/cus-faq-2.html#Q24 . Die erste Methode oben ist fast immer die beste Option und hat die offensichtlichste Semantik.

Ed Morton
quelle
6

Sie können die Befehlszeilenoption -v mit einem Variablennamen ( v) und einem Wert ( =) der Umgebungsvariablen ( "${v}") übergeben:

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

Oder um es klarer zu machen (mit weit weniger vs):

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

Sie können ARGV verwenden:

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

Beachten Sie, dass Sie ARGC anpassen müssen, wenn Sie mit dem Körper fortfahren möchten:

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

Ich habe gerade @ Jotnes Antwort für "for loop" geändert.

for i in `seq 11 20`; do host myserver-$i | awk -v i="$i" '{print "myserver-"i" " $4}'; done
edib
quelle
1
Dies scheint lediglich ein weiteres Beispiel für die Verwendung der -vOption von Awk zu sein, die bereits in vielen der vorhandenen Antworten erwähnt wurde. Wenn Sie zeigen möchten, wie Awk in einer Schleife ausgeführt wird, ist das wirklich eine andere Frage.
Tripleee
0

Ich musste das Datum am Anfang der Zeilen einer Protokolldatei einfügen und es geschieht wie folgt:

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

Es kann zum Speichern in eine andere Datei umgeleitet werden

Sina
quelle
Das doppelte Anführungszeichen - einfaches Anführungszeichen - doppeltes Anführungszeichen war genau das, was ich brauchte, damit meine Arbeit funktioniert.
user53029
2
Dies wurde bereits in der akzeptierten Antwort als Methode erwähnt, die Sie aufgrund von Sicherheitslücken bei der Codeinjektion nicht verwenden sollten. Die Informationen hier sind also redundant (bereits in der akzeptierten Antwort beschrieben) und unvollständig (erwähnt die Probleme mit dieser Methode nicht).
Jason S