Usando la función bash shell dentro de AWK

¿Es posible usar la función bash dentro de AWK de alguna manera?

Archivo de ejemplo (string, int, int , int)

Mike 247808 247809 247810 

Intentando convertir valores de decimal a hexadecimal.

Función definida en .bashrc o en un script de shell.

$ awk "{print $1 ; d2h($2)}" file awk: calling undefined function d2h input record number 1, file file source line number 1 

Responder

Intente use la función system():

awk "{printf("%s ",$1); system("d2h " $2)}" file 

En su caso, system llamar a d2h 247808 y luego agregar la salida de este comando a la printf salida:

Mike 3C800 

EDITAR:

Como system usa sh en lugar de bash No puedo encontrar una manera de acceder a .bashrc. Pero aún puede usar funciones de su script bash actual:

#!/bin/bash d2h() { # do some cool conversion here echo "$1" # or just output the first parameter } export -f d2h awk "{printf("%s ",$1); system("bash -c "\""d2h "$2""\""")}" file 

EDICIÓN 2:

No lo sé por qué, pero esto no funciona en mi Ubuntu 16.04. Esto es extraño, porque solía funcionar en Ubuntu 14.04.

Comentarios

  • Esto podría funcionar si d2h eran ejecutables, pero no si eran ‘ una función » definida en .bashrc o en un script de shell «.
  • @MichaelHomer sí, tienes razón. Gracias a tu comentario me di cuenta de que he respondido la pregunta que nadie hizo. Pero encontré una manera de usar funciones del script actual (y posiblemente de otros scripts a través de source) pero puedo ‘ f figure averiguar por qué no ‘ no funciona con .bashrc.
  • Eso ‘ s también una vulnerabilidad de inyección de comando ya que el contenido de $2 termina siendo interpretado como código de shell.
  • Si export -f d2h doesn ‘ t funciona, puede hacerlo como para ksh / zsh: (\ 42 y \ 47 son los códigos de escape para comillas simples y dobles) CODE=$(typeset -f d2h) awk ' { system("bash -c \47eval \42$CODE\42; d2h \42"$2"\42\47") }'

Responder

Puede llamar a bash desde awk y usar su salida. Eso es obviamente peligroso desde la perspectiva del desempeño si ocurre con demasiada frecuencia. Citando la página del manual:

command | getline [var] 

Ejecute el comando canalizando la salida a $ 0 o var,

El comando sería un script bash que contiene la definición de la función y ejecuta la función.

Respuesta

Convertir de decimal a hexadecimal es algo que awk puede hacer muy bien por sí mismo. Y podría definir una función awk para hacerlo:

 function d2h(d) { return sprintf("%x", d) }  

Ahora, para responder a la pregunta en el caso general, awk para ejecutar bash funciones, «necesitarías awk para ejecutar un bash shell, que bash para interpretar la definición de esa función, y llamar a esa función, con el valor extraído por awk pasado como argumentos.

No es trivial.

bash admite la exportación de funciones a través del entorno, por lo que está disponible en invocaciones posteriores de bash, por lo que es una forma de pasar la definición de la función a la bash invocada por awk:

export -f d2h 

Las únicas formas de que awk ejecute un comando (bash h ere) son con su system("cmd"), o print... | "cmd" o "cmd" | getline. En todos los casos, awk ejecuta un shell para interpretar que cmd, pero será sh, no bash. Por lo tanto, debe construir una línea de comando para sh que sea una invocación bash que interprete una bash línea de comando para invocar la función, por lo que debe tener cuidado con las comillas:

export -f d2h <file awk -v q=""" " function shquote(s) { gsub(q, q "\\" q q, s) return q s q } {print $1; system("exec bash -c "\""d2h \"$1\""\"" bash " shquote($2))}" 

Si desea obtener la salida de la función de nuevo en awk, necesitaría transferirlo de nuevo a través de una tubería. Para eso, «usaría cmd | getline en lugar de system(cmd) (que deja la salida estándar de cmd» sin tocar).

cmd | getline line almacena una línea (estrictamente hablando un registro , los registros son líneas por defecto), así que para obtener el resultado completo en los casos en que esté formado por varias líneas, necesitará un bucle como:

 awk "... cmd = "exec bash -c "\""d2h \"$1\""\"" bash " shquote($2) output = "" while ((cmd | getline line) > 0) { output = output line RS } sub(RS "$", "", output) # remove the last newline ..."  

Eso significa ejecutar una sh y una bash para cada invocación de la función , por lo que va a ser bastante ineficiente. Eso terminaría siendo significativamente más ineficiente que tener bash haciendo la lectura y dividiendo con un while read loop:

(unset -v IFS; while read -r a b rest; do printf "%s\n" "$a" d2h "$b" done < file) 

También tenga en cuenta que desde shellshock, bash ahora exporta funciones en variables de entorno que se denominan BASH_FUNC_d2h%%. Algunas sh implementaciones, incluidas mksh y versiones más recientes de dash eliminar esas variables de entorno del entorno:

$ env "foo%%=bar" dash -c "printenv foo%%" $ env "foo%%=bar" mksh -c "printenv foo%%" $ env "foo%%=bar" zsh -c "printenv foo%%" bar $ env "foo%%=bar" bash -c "printenv foo%%" bar 

Entonces, en lugar de depender de la endeble función de exportación de funciones, podría pasar la definición de función de alguna otra manera. Podría ser a través de una variable de entorno con un nombre habitual:

 BASH_FUNCTIONS=$(typeset -f d2h) awk " ... cmd = "exec bash -c "\""eval \"$BASH_FUNCTIONS\";" \ "d2h \"$1\""\"" bash " shquote($2) ..."  

Responder

Intenta hacer esto:

awk "{print $1 ; printf "%x\n", $2}" file 

AFAIK, no puedes usar un bash función en awk pero solo un script. Puede usar una función awk si es necesario.

Responder

Usando una función bash definida por el usuario dentro de awk

Descargo de responsabilidad: me doy cuenta de que esto no es lo que el OP está tratando de hacer, pero Google llevará a otros como yo a esta respuesta.

Situación

Tienes un bash script que está organizado con funciones (porque no te odias a ti mismo ni a [la mayoría] de tus compañeros de trabajo) y al menos una de esas funciones debe llamar a otro desde awk.

Solución

Script

#!/bin/env bash # The main function - it"s a sound pattern even in BASH main(){ # In the awk command I do some tricky things with single quotes. Count carefully... # The first $0 is outside the single quotes so it is the name of the current bash script. # The second $0 is inside the single quotes so it is awk"s current line of input. awk "{printf("%s. ", ++c); system(""$0" --do"); print $0}"<<-PRETEND_THIS_IS_AN_INPUT_STREAM and and well PRETEND_THIS_IS_AN_INPUT_STREAM } # functionized to keep things DRY doit(){ echo -n "doin" it " } # check for a command switch and call different functionality if it is found if [[ $# -eq 1 && $1 == "--do" ]]; then doit else main fi 

Salida

$ ./example.sh 1. doin" it and 2. doin" it and 3. doin" it well 

Comentarios

  • yo representar a Queens, se crió en Brooklyn

Responder

Solo un ejemplo rápido para demostrar @HaukeLaging «s command|getline:

  1. sea la entrada:
Dear friends my name is `id -nu` and today is `date "+%Y-%m-%d"`. 

donde estamos siguiendo la sintaxis de shell, en la entrada,

`command` 

se usa para denotar comandos en línea para ser reemplazados por el resultado de su ejecución.

  1. Podemos expandir el comando de shell en línea mediante:
#!/usr/bin/gawk -f BEGIN { FS ="`"; } NF==3 { $2 | getline $2 } { print } 
  1. Uso (después del chmod habitual):
$ expand-inline input Dear friends my name is jjoao and today is 2018-01-15. 

Respuesta

Esto le dará buenas oportunidades.

cat ../logs/em2.log.1 |grep -i 192.168.21.15 |awk "{system("date"); print $1}" 

La función del sistema le permite analizar el comando bash dentro del flujo awk.

Respuesta

Esta respuesta es para GNU awk, pero otras versiones pueden implementar el mismo truco. La idea es que mientras el lado derecho de la tubería sea exactamente el mismo y no salga, awk no cerrará la tubería. Pero si es diferente, abrirá una nueva tubería. Por ejemplo, si escribe:

awk "BEGIN{ shell= "/bin/bash" } { print $0 | "/bin/bash" } END { print "echo $a" | " /bin/bash" }" 

puede escribir:

a=5 echo $a 

y obtenga la respuesta esperada (?) 5: no se generó un nuevo shell. Pero en la sección END, escribí un espacio adicional, por lo que el RHS de la tubería es diferente, y se genera un nuevo shell sin valor para la variable a (y por lo tanto imprimirá una línea en blanco al salir. No puedo encontrarlo en documentación, pero la forma recomendada de usar tuberías a largo plazo es la que tengo: poner el comando en la sección BEGIN en una variable y reutilizar esa variable.

Dar una función definida por el usuario a una sesión de bash y luego, reutilizarlo es un ejercicio que le queda al lector 🙂

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *