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

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

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

Archivo de ejemplo (cadena, int, int, int)

Mike 247808 247809 247810

Intentando convertir valores de decimal a hexadecimal.

Función definida en .bashrco en el script de shell.

$ awk '{print $1 ; d2h($2)}' file

awk: calling undefined function d2h
 input record number 1, file file
 source line number 1

Respuesta1

Intente utilizar system()la función:

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

En su caso, systemllamará d2h 247808y luego agregará la salida de este comando a printfla salida:

Mike 3C800

EDITAR:

Como systemusos shen lugar de bashno encuentro la manera de acceder .bashrc. Pero aún puedes usar funciones de tu 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

Nota: -fse utiliza paraexportar una funciónen lugar de una variable.

EDITAR 2:

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

Respuesta2

Puedes llamar a bash desde awk y usar su salida. Obviamente, esto es peligroso desde una perspectiva de rendimiento si sucede con demasiada frecuencia. Citando la página de 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.

Respuesta3

La conversión de decimal a hexadecimal es algo que awkmuy bien puede hacerse por sí solo. Y podrías definir unawkfunción para hacerlo:

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

Ahora, para responder a la pregunta en el caso general, para awkejecutar bashfunciones, necesitaría awkejecutar un bashshell, que bashinterpretaría la definición de esa función y llamaría a esa función, con el valor extraído awkpasado como argumentos.

No trivial.

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

export -f d2h

Las únicas formas de awkejecutar un comando ( bashaquí) son con su system("cmd")o print... | "cmd"o "cmd" | getline. En todos los casos, awkse ejecuta un shell para interpretar eso cmd, pero así será sh, no bash. Entonces necesitas construir una línea de comando porque shes una bashinvocación que interpreta una bashlínea de comando para invocar la función, por lo que debes tener cuidado al citar:

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 recuperar la salida de la función awk, deberá transferirla nuevamente a través de una tubería. Para eso, usarías cmd | getlineen lugar de system(cmd)(lo que deja cmdla salida estándar intacta).

cmd | getline linehistoriasuna línea(estrictamente hablandoun registro, los registros son líneas de forma predeterminada), por lo que para obtener el resultado completo en los casos en que esté formado por varias líneas, necesitaría 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 uno shy otro bashpara cada invocación de la función, por lo que será bastante ineficiente. Eso terminaría siendo incluso significativamente más ineficiente que tener bashque leer y dividir 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, bashahora exporta funciones en variables de entorno con nombres como BASH_FUNC_d2h%%. Algunas shimplementaciones que incluyen mkshversiones más recientes dedash eliminaresas 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 confiar en la endeble función de exportación de funciones, podría pasar la definición de la función de otra manera. Podría ser mediante 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)
   ...'

Respuesta4

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 intenta hacer, pero Google guiará a otros como yo a esta respuesta.

Situación

Tienes un bashscript 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 necesita llamar a otra desde dentro awk.

Solución

Guion

#!/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

Producción

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

información relacionada