¿Cómo uso las variables de shell en un script awk?

289

Encontré algunas formas de pasar variables de shell externas a un awkscript, pero estoy confundido acerca de 'y ".

Primero, probé con un script de shell:

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

Luego probé awk:

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

¿Por qué es la diferencia?

Por último probé esto:

$ 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 

Estoy confundido acerca de esto.

hqjma
fuente
2
Me gusta el -v como se muestra a continuación, pero este es realmente un gran ejercicio para pensar en cómo proteger las cosas del shell. Trabajando a través de esto, mi primer corte usa barras invertidas en espacios y signos de dólar. No hace falta decir que los ejemplos aquí valieron la pena.
Chris
Si su búsqueda awk necesita una expresión regular , no puede poner /var/. En cambio, use tilde:awk -v var="$var" '$0 ~ var'
Noam Manos

Respuestas:

496

Obtener variables de shell en awk

Se puede hacer de varias maneras. Algunos son mejores que otros. Esto debería cubrir la mayoría de ellos. Si tienes un comentario, déjalo a continuación. v1.5


Usando -v (La mejor manera, más portátil)

Use la -vopción: (PS usa un espacio después -vo será menos portátil. Por ejemplo, awk -v var=no awk -vvar=)

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

Esto debería ser compatible con la mayoría awk, y la variable también está disponible en el BEGINbloque:

Si tienes múltiples variables:

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

Advertencia . Como escribe Ed Morton, las secuencias de escape se interpretarán, por lo que se \tconvierte en algo real taby no \tsi eso es lo que buscas. Se puede resolver mediante el uso ENVIRON[]o el acceso a través deARGV[]

PD: si le gustan tres barras verticales como separador |||, no se puede escapar, así que use-F"[|][|][|]"

Ejemplo sobre cómo obtener datos de un programa / función inn a awk(aquí se usa la fecha)

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

Variable después del bloque de código

Aquí obtenemos la variable después del awkcódigo. Esto funcionará bien siempre que no necesite la variable en el BEGINbloque:

variable="line one\nline two"
echo "input data" | awk '{print var}' var="${variable}"
or
awk '{print var}' var="${variable}" file
  • Agregar múltiples variables:

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

  • De esta manera, también podemos establecer diferentes separadores de campo FSpara cada archivo.

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

  • La variable después del bloque de código no funcionará para el BEGINbloque:

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


Here-string

La variable también se puede agregar al awkuso de una cadena aquí de los shells que los admiten (incluido Bash):

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

Esto es lo mismo que:

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

PD: trata la variable como una entrada de archivo.


ENVIRON entrada

Como TrueY escribe, puede usar ENVIRONpara imprimir Variables de entorno . Al configurar una variable antes de ejecutar AWK, puede imprimirla así:

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

ARGV entrada

Como escribe Steven Penny, puede usar ARGVpara obtener los datos en awk:

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

Para obtener los datos en el propio código, no solo el COMIENZO:

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

Variable dentro del código: USO CON PRECAUCIÓN

Puede usar una variable dentro del awkcódigo, pero es desordenada y difícil de leer, y como Charles Duffyseñala, esta versión también puede ser víctima de la inyección de código. Si alguien agrega cosas malas a la variable, se ejecutará como parte del awkcódigo.

Esto funciona extrayendo la variable dentro del código, por lo que se convierte en parte de él.

Si desea hacer un awkcambio dinámico con el uso de variables, puede hacerlo de esta manera, pero NO lo use para variables normales.

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

Aquí hay un ejemplo de inyección de código:

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

Puede agregar muchos comandos de awkesta manera. Incluso haz que se bloquee con comandos no válidos.


Información extra:

Uso de comillas dobles

Siempre es bueno comillas dobles. "$variable"
Si no, se agregarán varias líneas como una sola línea larga.

Ejemplo:

var="Line one
This is line two"

echo $var
Line one This is line two

echo "$var"
Line one
This is line two

Otros errores que puede obtener sin comillas dobles:

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

Y con comillas simples, no expande el valor de la variable:

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

Más información sobre AWK y variables

Lee este faq .

Jotne
fuente
2
"desordenado y difícil de leer" ignora la preocupación de seguridad más importante de la inyección de código al sustituir directamente cadenas en código awk.
Charles Duffy
leyendo la respuesta anterior, puedo ejecutar mi script sin errores, pero no funciona: 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. ¿Es por el paréntesis anidado {?
Darion Badlydone
@DarionBadlydone Prueba esto awk -v repo="$1" -v tag="$2" 'BEGIN {print "repo="repo,"tag="tag}'. Verá si imprime la variable. Publique una pregunta propia si no puede resolverlo.
Jotne
@Jotne sí, imprime los valores, así que lo intenté de esta manera: awk -v repo = "$ 1" -v tag = "$ 2" '{print "{sub (/ image: registryabc.azurecr.io/"repo" :( [a-z0-9] +) $ /, \ "imagen: registryabc.azurecr.io/"repo":"tag"\");}1"} './services/appscompose.yaml >> newcompose.yaml pero no funciona como se esperaba Reemplaza cada línea del archivo fuente con el comando impreso
Darion Badlydone
@Jotne Lo hice con sed, gracias de todos modos
Darion Badlydone
28

Parece que la buena vieja ENVIRON el hash incorporado no se menciona en absoluto. Un ejemplo de su uso:

$ X=Solaris awk 'BEGIN{print ENVIRON["X"], ENVIRON["TERM"]}'
Solaris rxvt
Verdad
fuente
44
Esta es una buena sugerencia porque pasa los datos al pie de la letra. -vno funciona cuando el valor contiene barras invertidas.
ese otro tipo
2
@thatotherguy ¡No lo sabía! Pensé que si lo uso awk -v x='\c\d' ..., lo usaré correctamente. Pero cuando xse imprime awk cae el famoso: awk: warning: escape sequence '\c' treated as plain 'c'mensaje de error ... ¡Gracias!
Verdaderamente
Funciona correctamente: en este contexto, significa expandir las secuencias de escape porque así -vfue diseñado para funcionar para que pueda usar \ten la variable y hacer que coincida con una pestaña literal en los datos, por ejemplo. Si ese no es el comportamiento que desea, entonces no lo use -v, use ARGV[]o ENVIRON[].
Ed Morton
9

Utilice cualquiera de estos dependiendo de cómo desee barras invertidas en las variables de shell manejadas ( avares una variable awk, svares una variable de shell):

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

Consulte http://cfajohnson.com/shell/cus-faq-2.html#Q24 para obtener detalles y otras opciones. El primer método anterior es casi siempre su mejor opción y tiene la semántica más obvia.

Ed Morton
fuente
6

Puede pasar la opción de línea de comandos -v con un nombre de variable ( v) y un valor ( =) de la variable de entorno ( "${v}"):

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

O para hacerlo más claro (con muchos menos vs):

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

Puede utilizar ARGV:

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

Tenga en cuenta que si va a continuar dentro del cuerpo, deberá ajustar ARGC:

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

Acabo de cambiar la respuesta de @ Jotne para "for loop".

for i in `seq 11 20`; do host myserver-$i | awk -v i="$i" '{print "myserver-"i" " $4}'; done
edib
fuente
1
Esto simplemente parece ser otra ilustración de cómo usar la -vopción de Awk que ya se mencionó en muchas de las respuestas existentes. Si desea mostrar cómo ejecutar Awk en un bucle, esa es una pregunta realmente diferente.
tripleee
0

Tuve que insertar la fecha al comienzo de las líneas de un archivo de registro y se hace de la siguiente manera:

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

Se puede redirigir a otro archivo para guardar

Sina
fuente
La comilla doble - comilla simple - comilla doble era exactamente lo que necesitaba para que la mía funcionara.
user53029
2
Esto ya se mencionó en la respuesta aceptada como un método que no debe usar debido a las vulnerabilidades de inyección de código. Entonces, la información aquí es redundante (ya descrita en la respuesta aceptada) e incompleta (no menciona los problemas con este método).
Jason S