Usando uma função bash definida pelo usuário dentro do awk

Usando uma função bash definida pelo usuário dentro do awk

É possível usar a função bash dentro do AWK de alguma forma?

Arquivo de exemplo (string, int, int, int)

Mike 247808 247809 247810

Tentando converter valores de decimal para hexadecimal.

Função definida em .bashrcou em shell script.

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

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

Responder1

Tente usar system()a função:

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

No seu caso system, chamará d2h 247808e anexará a saída deste comando à printfsaída:

Mike 3C800

EDITAR:

Como systemusa shem vez de bashnão consigo encontrar uma maneira de acessar o arquivo .bashrc. Mas você ainda pode usar funções do seu script bash atual:

#!/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: -fé usado paraexportar uma funçãoem vez de uma variável.

EDITAR 2:

Não sei por que, mas isso não está funcionando no meu Ubuntu 16.04. Isso é estranho, porque funcionava no Ubuntu 14.04.

Responder2

Você pode chamar o bash do awk e usar sua saída. Isso é obviamente perigoso do ponto de vista do desempenho se acontecer com muita frequência. Citando a página de manual:

command | getline [var]

Execute o comando canalizando a saída para $ 0 ou var,

comando seria um script bash que contém a definição da função e executa a função.

Responder3

Converter de decimal para hexadecimal é algo que awkpode muito bem ser feito sozinho. E você poderia definir umawkfunção para fazer isso:

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

Agora, para responder à pergunta no caso geral, para awkexecutar bashfunções, você precisaria awkexecutar um bashshell, para bashinterpretar a definição daquela função, e chamar essa função, com o valor extraído awkpassado como argumentos.

Não é trivial.

bashsuporta a exportação de funções através do ambiente, portanto está disponível em invocações subsequentes de bash, então essa é uma maneira de passar a definição da função para o bashinvocado por awk:

export -f d2h

As únicas maneiras de awkexecutar um comando ( bashaqui) são com seu system("cmd"), ou print... | "cmd"ou "cmd" | getline. Em todos os casos, awkexecuta um shell para interpretar isso cmd, mas será sh, não bash. Portanto, você precisa construir uma linha de comando, pois shé uma bashinvocação que interpreta uma bashlinha de comando para invocar a função, portanto, você precisa ter cuidado ao 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))}'

Se você quiser recuperar a saída da função awk, precisará transferi-la de volta por meio de um canal. Para isso, você usaria cmd | getlineem vez de system(cmd)(o que deixa cmdo stdout intocado).

cmd | getline linelojasuma linha(estritamente falandoum registro, os registros são linhas por padrão), portanto, para obter a saída completa nos casos em que ela é composta por várias linhas, você precisaria de um loop 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
  ...'

Isso significa executar um she um bashpara cada invocação da função, portanto será bastante ineficiente. Isso acabaria sendo ainda significativamente mais ineficiente do que fazer basha leitura e a divisão com um while read loop:

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

Observe também que, desde o shellshock, bashagora exporta funções em variáveis ​​de ambiente denominadas como BASH_FUNC_d2h%%. Algumas shimplementações, incluindo mkshversões mais recentes dedash removeressas variáveis ​​de ambiente do ambiente:

$ 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

Portanto, em vez de confiar no frágil recurso de exportação de função, você poderia passar a definição da função de outra maneira. Poderia ser através de uma variável de ambiente com um nome usual:

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

Responder4

Usando uma função bash definida pelo usuário dentro do awk

Isenção de responsabilidade:Sei que não é isso que o OP está tentando fazer, mas o Google levará outros como eu a essa resposta.

Situação

Você tem um bashscript organizado com funções (porque você não odeia a si mesmo ou à [maioria] dos colegas de trabalho) e pelo menos uma dessas funções precisa chamar outra de dentro do awk.

Solução

Roteiro

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

Saída

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

informação relacionada