Bruke bash shell-funksjon i AWK

Er det mulig å bruke bash-funksjon inne i AWK på en eller annen måte?

Eksempelfil (streng, int, int , int)

Mike 247808 247809 247810 

Prøver å konvertere verdier fra desimal til heksadesimal.

Funksjon definert enten i .bashrc eller i skallskript.

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

Svar

Prøv å bruk system() -funksjon:

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

I ditt tilfelle vil system ring d2h 247808 og legg deretter utgangen av denne kommandoen til printf output:

Mike 3C800 

EDIT:

Som system bruker sh i stedet for bash Jeg kan ikke finne en måte å få tilgang til .bashrc. Men du kan fortsatt bruke funksjoner fra det nåværende bash-skriptet:

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

Jeg vet ikke hvorfor, men dette fungerer ikke på min Ubuntu 16.04. Dette er rart, fordi det pleide å fungere på Ubuntu 14.04.

Kommentarer

  • Dette kan fungere hvis d2h var en kjørbar, men ikke hvis den ‘ sa » -funksjonen definerte enten i .bashrc eller i skallskript «.
  • @MichaelHomer ja, du har rett. Takket være din kommentar skjønte jeg at jeg har svart på spørsmålet ingen stilte. Men jeg har funnet en måte å bruke funksjoner fra det aktuelle skriptet (og muligens fra andre skript via source), men jeg kan ‘ f figur ut hvorfor det ikke fungerer ‘ med .bashrc.
  • At ‘ er også et sårbarhet med kommandainjeksjon som innholdet i $2 ender med å bli tolket som skallkode.
  • Hvis export -f d2h fungerer ikke ‘, du kan gjøre det som for ksh / zsh: (\ 42 og \ 47 er rømningskodene for enkle og doble anførselstegn) CODE=$(typeset -f d2h) awk ' { system("bash -c \47eval \42$CODE\42; d2h \42"$2"\42\47") }'

Svar

Du kan ringe bash fra awk og bruke utdataene. Det er åpenbart farlig fra et ytelsesperspektiv hvis det skjer for ofte. Sitere mannssiden:

command | getline [var] 

Kjør kommandorørutgangen enten til $ 0 eller var,

kommando ville være et bash-skript som inneholder funksjonsdefinisjonen og utfører funksjonen.

Svar

Konvertering fra desimal til heksadesimal er noe som awk veldig godt kan gjøre selv. Og du kan definere en awk funksjon for å gjøre det:

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

Nå for å svare på spørsmålet generelt, for awk for å kjøre bash funksjoner, du «trenger awk for å utføre et bash skall, det bash for å tolke definisjonen av den funksjonen, og kalle den funksjonen, med verdien ekstrahert av awk gitt som argumenter.

Ikke trivielt.

bash støtter eksportfunksjoner via miljøet, slik at det er tilgjengelig i påfølgende påkallelser av bash, slik at det er en måte å passere definisjonen av funksjonen til bash påkalt av awk:

export -f d2h 

De eneste måtene for awk å utføre en kommando (bash h ere) er med sin system("cmd"), eller print... | "cmd" eller "cmd" | getline. I alle tilfeller kjører awk et skall for å tolke at cmd, men det vil være sh, ikke bash. Så du må lage en kommandolinje for sh som er en bash påkallelse som tolker en bash kommandolinje for å påkalle funksjonen, så du må være forsiktig med å sitere:

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

Hvis du vil få utgangen av funksjonen tilbake til awk, du trenger å overføre den tilbake via et rør. For det bruker du cmd | getline i stedet for system(cmd) (som lar cmd» s stdout uberørt).

cmd | getline line lagrer en linje (strengt tatt en post , postene er linjer som standard), så for å få hele produksjonen i tilfeller der den er laget av flere linjer, trenger du en sløyfe 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 betyr å kjøre en sh og en bash for hver hver påkallelse av funksjonen , så kommer til å være ganske ineffektivt. Det vil ende opp med å bli enda mer ineffektivt enn å ha bash til å lese og dele med en while read loop:

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

Vær også oppmerksom på at siden shellshock eksporterer bash nå funksjoner i miljøvariabler som heter BASH_FUNC_d2h%%. Noen sh implementeringer inkludert mksh og nyere versjoner av dash fjern disse miljøvariablene fra miljøet:

$ 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å i stedet for å stole på eksportfunksjonen for spinkel funksjon, kan du passere funksjonsdefinisjonen på en annen måte. Det kan være via en miljøvariabel med et vanlig navn:

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

Svar

Prøv å gjøre dette:

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

AFAIK, du kan ikke bruke en bash funksjon i awk, men bare et skript. Du kan bruke en awk-funksjon om nødvendig.

Svar

Ved å bruke en brukerdefinert bash-funksjon inside awk

Ansvarsfraskrivelse: Jeg skjønner at dette ikke er hva OP prøver å gjøre, men Google vil lede andre som meg til dette svaret.

Situasjon

Du har et bash skript som er organisert med funksjoner (fordi du ikke hater deg selv eller [de fleste] kolleger) og minst én av disse funksjonene må ring en annen innenfra 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

  • jeg representer Queens, hun ble oppvokst i Brooklyn

Svar

Bare et raskt eksempel for å demonstrere @HaukeLaging «s command|getline:

  1. la input være:
Dear friends my name is `id -nu` and today is `date "+%Y-%m-%d"`. 

der vi følger skallsyntaks, i inngangen, brukes

`command` 

til å betegne innebygde kommandoer som skal erstattes av resultatet av hans henrettelse.

  1. Vi kan utvide inline shell-kommando med:
#!/usr/bin/gawk -f BEGIN { FS ="`"; } NF==3 { $2 | getline $2 } { print } 
  1. Bruk (etter vanlig chmod):
$ expand-inline input Dear friends my name is jjoao and today is 2018-01-15. 

Svar

Dette gir deg gode sjanser.

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

systemfunksjonen gjør at du kan analysere bash-kommandoen i awk stream.

Svar

Dette svaret hvis det gjelder GNU awk, men andre versjoner kan implementere det samme trikset. Tanken er at så lenge den høyre siden av røret er nøyaktig den samme og ikke kommer ut, lukker ikke awk røret. Men hvis det er annerledes, vil det åpne et nytt rør. Hvis du for eksempel skriver:

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

kan du skrive:

a=5 echo $a 

og få forventet (?) respons 5: ingen nye skall ble gytt. Men i SLUTT-seksjonen skrev jeg inn et ekstra mellomrom, slik at rørets RHS er annerledes, og et nytt skall er gytt uten verdi for variabel a (og vil dermed skrive ut en tom linje ved utgangen. Jeg finner den ikke i dokumentasjon, men den anbefalte måten å bruke langsiktige rør på er som jeg har: legg kommandoen i BEGIN-delen i en variabel, og bruk den variabelen på nytt.

Gi en brukerdefinert funksjon til en bash-økt og å gjenbruke det er en øvelse overlatt til leseren 🙂

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *