Använda bash-skalfunktion inuti AWK

Är det möjligt att använda bash-funktion inuti AWK på något sätt?

Exempelfil (sträng, int, int , int)

Mike 247808 247809 247810 

Försöker konvertera värden från decimal till hexadecimal.

Funktion definierad antingen i .bashrc eller i skalskript.

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

Svar

Försök att använd system() -funktion:

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

I ditt fall system ring d2h 247808 och lägg sedan till kommandos utdata till printf output:

Mike 3C800 

EDIT:

Eftersom system använder sh istället för bash Jag kan inte hitta ett sätt att komma åt .bashrc. Men du kan fortfarande använda funktioner från ditt nuvarande bash-skript:

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

Jag vet inte varför, men detta fungerar inte på min Ubuntu 16.04. Det här är konstigt eftersom det brukade fungera på Ubuntu 14.04.

Kommentarer

  • Detta kan fungera om d2h var en körbar, men inte om den ’ sa ” -funktionen definierad antingen i .bashrc eller i skalskript ”.
  • @MichaelHomer ja, du har rätt. Tack vare din kommentar insåg jag att jag har svarat på den fråga som ingen ställde. Men jag har hittat ett sätt att använda funktioner från det aktuella skriptet (och eventuellt från andra skript via source) men jag kan ’ f figurera varför det inte fungerar ’ med .bashrc.
  • Att ’ s också en sårbarhet med kommandainjektion eftersom innehållet i $2 slutar tolkas som skalkod.
  • Om export -f d2h fungerar inte ’, du kan göra det som för ksh / zsh: (\ 42 och \ 47 är flyktkoder för enkla och dubbla citat) CODE=$(typeset -f d2h) awk ' { system("bash -c \47eval \42$CODE\42; d2h \42"$2"\42\47") }'

Svar

Du kan ringa bash från awk och använda dess utdata. Det är uppenbarligen farligt ur ett prestationsperspektiv om det händer för ofta. Citera man-sidan:

command | getline [var] 

Kör kommandot som rör utgången antingen till $ 0 eller var,

-kommandot skulle vara ett bash-skript som innehåller funktionsdefinitionen och kör funktionen.

Svar

Konvertering från decimal till hexadecimal är något som awk mycket väl kan göra själv. Och du kan definiera en awk -funktion för att göra det:

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

Nu för att svara på frågan i allmänhet, för att awk ska köra bash funktioner, du ”behöver awk för att köra ett bash skal, det bash för att tolka definitionen av den funktionen och kalla den funktionen med värdet extraherat av awk skickat som argument.

Inte trivialt.

bash stöder exportfunktioner via miljön, så den är tillgänglig i efterföljande anrop av bash, så att ”ett sätt att passera definitionen av funktionen till bash åberopad av awk:

export -f d2h 

De enda sätten för awk att utföra ett kommando (bash h är) är med sin system("cmd"), eller print... | "cmd" eller "cmd" | getline. I alla fall kör awk ett skal för att tolka att cmd, men det blir sh, inte bash. Så du måste konstruera en kommandorad för sh som är en bash anrop som tolkar en bash kommandorad för att anropa funktionen, så du måste vara försiktig med att citera:

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))}" 

Om du vill få utdata från funktionen tillbaka till awk, du behöver överföra den via ett rör. För det använder du cmd | getline istället för system(cmd) (vilket lämnar cmd” s stdout orörd).

cmd | getline line lagrar en rad (strikt taget en post , poster är rader som standard), så för att få hela produktionen i de fall där den är gjord av flera rader, behöver du en slinga som:

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

Det betyder att man kör en sh och en bash för varje anrop av funktionen , så kommer det att bli ganska ineffektivt. Det skulle bli ännu mer ineffektivt än att bash läste och delade med en while read loop:

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

Observera också att sedan shellshock exporterar bash nu funktioner i miljövariabler som heter BASH_FUNC_d2h%%. Några sh implementeringar inklusive mksh och nyare versioner av dash ta bort dessa miljövariabler från miljön:

$ 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 

Så istället för att förlita dig på exportfunktionen för tunn funktion kan du skicka funktionsdefinitionen på något annat sätt. Det kan ske via en miljövariabel med ett vanligt namn:

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

Svar

Prova att göra detta:

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

AFAIK, du kan inte använda en bash funktion i awk men bara ett skript. Du kan använda en awk-funktion om det behövs.

Svar

Använda en användardefinierad bash-funktion inuti awk

Ansvarsfriskrivning: Jag inser att detta inte är vad OP försöker göra, men Google kommer att leda andra som jag till detta svar.

Situation

Du har ett bash -skript som är organiserat med funktioner (eftersom du inte hatar dig själv eller [de flesta] kollegor) och minst en av dessa funktioner behöver ring en annan inifrån awk.

Lösning

Skript

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

Output

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

Kommentarer

  • jag representera Queens, hon växte upp i Brooklyn

Svar

Bara ett snabbt exempel för att visa @HaukeLaging ”s command|getline:

  1. låt ingången vara:
Dear friends my name is `id -nu` and today is `date "+%Y-%m-%d"`. 

där vi följer skal-syntax i ingången,

`command` 

används för att beteckna inline-kommandon som ska ersättas med resultatet hans utförande.

  1. Vi kan utöka kommandot med inbyggt skal med:
#!/usr/bin/gawk -f BEGIN { FS ="`"; } NF==3 { $2 | getline $2 } { print } 
  1. Användning (efter vanligt chmod):
$ expand-inline input Dear friends my name is jjoao and today is 2018-01-15. 

Svar

Detta ger dig goda chanser.

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

systemfunktionen gör att du kan analysera bash-kommandot i awk stream.

Svar

Detta svar om för GNU awk, men andra versioner kan implementera samma trick. Tanken är att så länge den högra sidan av röret är exakt densamma och inte går ut, stänger awk inte röret. Men om det är annorlunda öppnar det ett nytt rör. Om du till exempel skriver:

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

kan du skriva:

a=5 echo $a 

och få det förväntade (?) svaret 5: inget nytt skal skapades. Men i END-avsnittet skrev jag ett extra utrymme, så rörets RHS är annorlunda och ett nytt skal skapas utan värde för variabel a (och kommer därmed att skriva ut en tom rad vid utgången. Jag kan inte hitta den i dokumentation, men det rekommenderade sättet att använda långsiktiga rör är som jag har: placera kommandot i BEGIN-sektionen i en variabel och återanvänd den variabeln.

Ge en användardefinierad funktion till en bash-session och sedan återanvändning är en övning som lämnas åt läsaren 🙂

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *