Utilizzo della funzione di shell bash allinterno di AWK

È possibile utilizzare la funzione bash allinterno di AWK in qualche modo?

File di esempio (string, int, int , int)

Mike 247808 247809 247810 

Tentativo di convertire i valori da decimale a esadecimale.

Funzione definita in .bashrc o in uno script di shell.

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

Answer

Prova a utilizza la funzione system():

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

Nel tuo caso system chiama d2h 247808 e aggiungi loutput di questo comando all printf output:

Mike 3C800 

MODIFICA:

poiché system utilizza sh invece di bash Non riesco “a trovare un modo per accedere a .bashrc. Ma puoi comunque utilizzare le funzioni del tuo attuale script bash:

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

EDIT 2:

Non lo so perché, ma questo non funziona sul mio Ubuntu 16.04. Questo è strano, perché prima funzionava su Ubuntu 14.04.

Commenti

  • Questo potrebbe funzionare se d2h fosse un eseguibile, ma non se fosse ‘ sa ” funzione definita in .bashrc o nello script di shell “.
  • @MichaelHomer sì, hai ragione. Grazie al tuo commento ho capito di aver risposto alla domanda che nessuno mi ha posto. Ma ho trovato un modo per utilizzare le funzioni dello script corrente (e possibilmente da altri script tramite source) ma posso ‘ figura capire perché ‘ t funziona con .bashrc.
  • Quello ‘ è anche una vulnerabilità di iniezione di comandi poiché il contenuto di $2 finisce per essere interpretato come codice della shell.
  • If export -f d2h ‘ non funziona, puoi farlo come per ksh / zsh: (\ 42 e \ 47 sono i codici di escape per virgolette semplici e doppie) CODE=$(typeset -f d2h) awk ' { system("bash -c \47eval \42$CODE\42; d2h \42"$2"\42\47") }'

Rispondi

Puoi chiamare bash da awk e usare il suo output. Questo è ovviamente pericoloso dal punto di vista delle prestazioni se accade troppo spesso. Citando la pagina man:

command | getline [var] 

Esegui il comando piping loutput in $ 0 o var,

sarebbe uno script bash che contiene la definizione della funzione ed esegue la funzione.

Risposta

La conversione da decimale a esadecimale è qualcosa che awk può fare molto bene da solo. E potresti definire una funzione awk per farlo:

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

Ora per rispondere alla domanda nel caso generale, per awk eseguire bash funzioni, è “necessario awk per eseguire una bash shell, che bash per interpretare la definizione di quella funzione e chiamare quella funzione, con il valore estratto da awk passato come argomenti.

Non banale.

bash supporta le funzioni di esportazione tramite lambiente, quindi è disponibile nelle successive chiamate di bash, quindi questo è un modo per passare la definizione della funzione per il bash invocato da awk:

export -f d2h 

Gli unici modi in cui awk può eseguire un comando (bash h ere) sono con il relativo system("cmd"), o print... | "cmd" o "cmd" | getline. In tutti i casi, awk esegue una shell per interpretarlo cmd, ma sarà sh, non bash. Quindi devi creare una riga di comando per sh che sia una bash invocazione che interpreta un bash riga di comando per richiamare la funzione, quindi devi stare attento con le virgolette:

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 vuoi recuperare loutput della funzione in awk, devi trasferirlo di nuovo tramite una pipe. Per questo, “d utilizzare cmd | getline invece di system(cmd) (che lascia lo stdout di cmd” inalterato).

cmd | getline line memorizza una riga (in senso stretto un record , i record sono righe per impostazione predefinita), quindi per ottenere lintero output nei casi in cui è composto da più righe, avresti bisogno di un ciclo come:

 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 ..."  

Ciò significa eseguire un sh e uno bash per ogni chiamata della funzione , quindi sarà piuttosto inefficiente. Sarebbe anche molto più inefficiente che avere bash leggere e suddividere con un while read loop:

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

Tieni inoltre presente che, a partire da shellshock, bash ora esporta funzioni in variabili di ambiente denominate come BASH_FUNC_d2h%%. Alcune sh implementazioni tra cui mksh e versioni più recenti di dash remove quelle variabili dambiente dallambiente:

$ 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 

Quindi, invece di fare affidamento sulla fragile funzione di esportazione della funzione, potresti passare la definizione della funzione in qualche altro modo. Potrebbe essere tramite una variabile di ambiente con un nome normale:

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

Risposta

Prova a farlo:

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

AFAIK, non puoi “usare una bash funzione in awk ma solo uno script. Puoi utilizzare una funzione awk se necessario.

Rispondi

Utilizzando una funzione bash definita dallutente inside awk

Dichiarazione di non responsabilità: mi rendo conto che questo non è ciò che lOP sta cercando di fare, ma Google condurrà altri come me a questa risposta.

Situazione

Hai uno bash script organizzato con funzioni (perché non odi te stesso o [la maggior parte] dei colleghi) e almeno una di queste funzioni deve chiamare un altro da awk.

Soluzione

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 

Risultato

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

Commenti

  • io rappresenta il Queens, è cresciuta a Brooklyn

Answer

Solo un rapido esempio per dimostrare @HaukeLaging “s command|getline:

  1. lascia che linput sia:
Dear friends my name is `id -nu` and today is `date "+%Y-%m-%d"`. 

dove stiamo seguendo la sintassi della shell, nellinput,

`command` 

è usato per denotare comandi inline da sostituire con il risultato di la sua esecuzione.

  1. Possiamo espandere il comando della shell inline con:
#!/usr/bin/gawk -f BEGIN { FS ="`"; } NF==3 { $2 | getline $2 } { print } 
  1. Utilizzo (dopo il solito chmod):
$ expand-inline input Dear friends my name is jjoao and today is 2018-01-15. 

Risposta

Questo ti darà buone possibilità.

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

la funzione di sistema ti consente di analizzare il comando bash allinterno di awk stream.

Risposta

Questa risposta è per GNU awk, ma altre versioni possono implementare lo stesso trucco. Lidea è che fintanto che il lato destro del tubo è esattamente lo stesso e non esce, awk non chiude il tubo. Ma se è diverso, aprirà un nuovo tubo. Ad esempio, se digiti:

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

puoi digitare:

a=5 echo $a 

e ottenere la risposta attesa (?) 5: nessuna nuova shell è stata generata. Ma nella sezione FINE, ho digitato uno spazio extra, quindi lRHS del tubo è diverso e viene generata una nuova shell senza alcun valore per la variabile a (e quindi stamperà una riga vuota alluscita. Non riesco a trovarla nella documentazione, ma il modo consigliato di usare pipe a lungo termine è come ho fatto io: inserire il comando nella sezione BEGIN in una variabile e riutilizzare quella variabile.

Dare una funzione definita dallutente a una sessione bash e quindi riutilizzarlo è un esercizio lasciato al lettore 🙂

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *