Comment utiliser les variables shell dans un script awk?

290

J'ai trouvé quelques façons de passer des variables de shell externes à un awkscript, mais je suis confus à propos de 'et ".

Tout d'abord, j'ai essayé avec un script shell:

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

Ensuite, j'ai essayé awk:

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

Pourquoi est la différence?

Enfin, j'ai essayé ceci:

$ 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 

Je suis confus à ce sujet.

hqjma
la source
2
J'aime le -v comme indiqué ci-dessous, mais c'est vraiment un excellent exercice pour réfléchir à la façon de protéger les choses du shell. En travaillant là-dessus, ma première coupe utilise des barres obliques inverses sur les espaces et les signes dollar. Inutile de dire que les exemples ici valaient bien mon temps.
Chris
Si votre recherche awk a besoin d' une expression régulière , vous ne pouvez pas le mettre /var/. À la place, utilisez le tilde:awk -v var="$var" '$0 ~ var'
Noam Manos

Réponses:

497

Obtention de variables shell dans awk

peut se faire de plusieurs manières. Certains sont meilleurs que d'autres. Cela devrait couvrir la plupart d'entre eux. Si vous avez un commentaire, veuillez laisser ci-dessous. v1.5


Utilisation -v (La meilleure façon, la plus portable)

Utilisez l' -voption: (PS utilisez un espace après -vou ce sera moins portable. Par exemple, awk -v var=pas awk -vvar=)

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

Cela devrait être compatible avec la plupart awk, et la variable est également disponible dans le BEGINbloc:

Si vous avez plusieurs variables:

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

Attention . Comme l'écrit Ed Morton, les séquences d'échappement seront interprétées et \tdeviennent donc un réel tabet non \tsi c'est ce que vous recherchez. Peut être résolu en l'utilisant ENVIRON[]ou en y accédant viaARGV[]

PS Si vous aimez trois barres verticales comme séparateur |||, il ne peut pas être échappé, alors utilisez-F"[|][|][|]"

Exemple sur l'obtention de données à partir d'un programme / fonction auberge awk(ici la date est utilisée)

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

Variable après bloc de code

Ici, nous obtenons la variable après le awkcode. Cela fonctionnera bien tant que vous n'avez pas besoin de la variable dans le BEGINbloc:

variable="line one\nline two"
echo "input data" | awk '{print var}' var="${variable}"
or
awk '{print var}' var="${variable}" file
  • Ajout de plusieurs variables:

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

  • De cette façon, nous pouvons également définir un séparateur de champs différent FSpour chaque fichier.

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

  • Variable après le bloc de code ne fonctionnera pas pour le BEGINbloc:

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


Here-string

Une variable peut également être ajoutée à l' awkutilisation d'une chaîne ici à partir de shells qui les prennent en charge (y compris Bash):

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

C'est la même chose que:

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

PS cela traite la variable comme une entrée de fichier.


ENVIRON contribution

Comme TrueY l'écrit, vous pouvez utiliser ENVIRONpour imprimer les variables d'environnement . En définissant une variable avant d'exécuter AWK, vous pouvez l'imprimer comme ceci:

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

ARGV contribution

Comme l'écrit Steven Penny, vous pouvez utiliser ARGVpour obtenir les données dans awk:

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

Pour obtenir les données dans le code lui-même, pas seulement le début:

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

Variable dans le code: UTILISER AVEC ATTENTION

Vous pouvez utiliser une variable dans le awkcode, mais c'est compliqué et difficile à lire, et comme le Charles Duffysouligne, cette version peut également être victime d'une injection de code. Si quelqu'un ajoute de mauvaises choses à la variable, elle sera exécutée dans le cadre du awkcode.

Cela fonctionne en extrayant la variable dans le code, elle en fait donc partie.

Si vous voulez faire un awkqui change dynamiquement avec l'utilisation de variables, vous pouvez le faire de cette façon, mais NE l'utilisez PAS pour des variables normales.

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

Voici un exemple d'injection de code:

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

Vous pouvez ajouter de nombreuses commandes de awkcette façon. Même le faire planter avec des commandes non valides.


Informaitons supplémentaires:

Utilisation de guillemets doubles

Il est toujours bon de doubler la variable de citation. "$variable"
Sinon, plusieurs lignes seront ajoutées sous la forme d'une longue ligne unique.

Exemple:

var="Line one
This is line two"

echo $var
Line one This is line two

echo "$var"
Line one
This is line two

Autres erreurs que vous pouvez obtenir sans guillemet double:

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

Et avec un guillemet simple, il n'augmente pas la valeur de la variable:

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

Plus d'informations sur AWK et les variables

Lisez cette FAQ .

Jotne
la source
2
"malpropre et difficile à lire" ignore le problème de sécurité le plus important de l'injection de code lors de la substitution directe de chaînes en code awk.
Charles Duffy
en lisant la réponse ci-dessus, je peux exécuter mon script sans erreurs mais il ne fait pas le travail: 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. Est-ce à cause de la parenthèse imbriquée {?
Darion Badlydone
@DarionBadlydone Essayez ceci awk -v repo="$1" -v tag="$2" 'BEGIN {print "repo="repo,"tag="tag}'. Il verra s'il imprime la variable. Postez votre propre question si vous ne pouvez pas le comprendre.
Jotne
@Jotne oui, il imprime les valeurs, j'ai donc essayé de cette façon: 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 mais ne fonctionne pas comme prévu. Il remplace chaque ligne du fichier source par la commande imprimée
Darion Badlydone
@Jotne Je l'ai fait avec sed, merci quand même
Darion Badlydone
28

Il semble que le bon vieux ENVIRON le hachage intégré n'est pas du tout mentionné. Un exemple de son utilisation:

$ X=Solaris awk 'BEGIN{print ENVIRON["X"], ENVIRON["TERM"]}'
Solaris rxvt
TrueY
la source
4
C'est une bonne suggestion car elle transmet les données textuellement. -vne fonctionne pas lorsque la valeur contient des barres obliques inverses.
cet autre gars
2
@thatotherguy je ne savais pas ça! Je pensais que si j'utilise awk -v x='\c\d' ...alors il sera utilisé correctement. Mais quand xest imprimé awk laisse tomber le fameux: awk: warning: escape sequence '\c' treated as plain 'c'message d'erreur ... Merci!
TrueY
Cela fonctionne correctement - correctement dans ce contexte signifie étendre les séquences d'échappement parce que c'est ainsi que cela a -vété conçu pour fonctionner afin que vous puissiez l'utiliser \tdans la variable et la faire correspondre à un onglet littéral dans les données, par exemple. Si ce n'est pas le comportement que vous souhaitez, vous n'utilisez pas -vvous utilisez ARGV[]ou ENVIRON[].
Ed Morton
9

Utilisez l'une de ces options en fonction de la manière dont vous souhaitez gérer les barres obliques inverses dans les variables de shell ( avarest une variable awk, svarest une variable de shell):

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

Voir http://cfajohnson.com/shell/cus-faq-2.html#Q24 pour plus de détails et d'autres options. La première méthode ci-dessus est presque toujours votre meilleure option et a la sémantique la plus évidente.

Ed Morton
la source
6

Vous pouvez passer l' option de ligne de commande -v avec un nom de variable ( v) et une valeur ( =) de la variable d'environnement ( "${v}"):

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

Ou pour être plus clair (avec beaucoup moins de vs):

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

Vous pouvez utiliser ARGV:

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

Notez que si vous allez continuer dans le corps, vous devrez ajuster l'ARGC:

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

Je viens de changer la réponse de @ Jotne pour "for loop".

for i in `seq 11 20`; do host myserver-$i | awk -v i="$i" '{print "myserver-"i" " $4}'; done
edib
la source
1
Cela semble simplement être une autre illustration de la façon d'utiliser l' -voption d'Awk qui a déjà été mentionnée dans de nombreuses réponses existantes. Si vous voulez montrer comment exécuter Awk en boucle, c'est vraiment une question différente.
tripleee
0

J'ai dû insérer la date au début des lignes d'un fichier journal et c'est fait comme ci-dessous:

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

Il peut être redirigé vers un autre fichier pour enregistrer

Sina
la source
La citation double - citation simple - citation double était exactement ce dont j'avais besoin pour faire fonctionner la mienne.
user53029
2
Cela a déjà été mentionné dans la réponse acceptée comme méthode que vous ne devriez pas utiliser en raison de vulnérabilités d'injection de code. Les informations sont donc redondantes (déjà décrites dans la réponse acceptée) et incomplètes (ne mentionnent pas les problèmes avec cette méthode).
Jason S