Brug af bash shell-funktion inde i AWK

Er det muligt at bruge bash-funktion inde i AWK på en eller anden måde?

Eksempelfil (streng, int, int , int)

Mike 247808 247809 247810 

Forsøger at konvertere værdier fra decimal til hexadecimal.

Funktion defineret enten i .bashrc eller i shell-script.

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

Svar

Prøv at brug system() -funktion:

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

I dit tilfælde vil system ring til d2h 247808 og tilføj derefter output fra denne kommando til printf output:

Mike 3C800 

EDIT:

Som system bruger sh i stedet for bash Jeg kan ikke finde en måde at få adgang til .bashrc. Men du kan stadig bruge funktioner fra dit nuværende bash-script:

#!/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 ved det ikke hvorfor, men dette fungerer ikke på min Ubuntu 16.04. Dette er underligt, fordi det plejede at fungere på Ubuntu 14.04.

Kommentarer

  • Dette kunne fungere, hvis d2h var en eksekverbar, men ikke hvis den ‘ sa ” funktion defineret enten i .bashrc eller i shell script “.
  • @MichaelHomer ja, du har ret. Takket være din kommentar indså jeg, at jeg har besvaret det spørgsmål, ingen stillede. Men jeg har fundet en måde at bruge funktioner fra det aktuelle script (og muligvis fra andre scripts via source), men jeg kan ‘ f figur ud hvorfor det ikke fungerer ‘ med .bashrc.
  • At ‘ er også en sårbarhed ved kommandoinjektion, da indholdet af $2 ender med at blive fortolket som shell-kode.
  • Hvis export -f d2h fungerer ikke ‘, du kan gøre det som for ksh / zsh: (\ 42 og \ 47 er flugtkoder til enkle og dobbelte citater) CODE=$(typeset -f d2h) awk ' { system("bash -c \47eval \42$CODE\42; d2h \42"$2"\42\47") }'

Svar

Du kan ringe til bash fra awk og bruge dens output. Det er naturligvis farligt set fra et præstationsperspektiv, hvis det sker for ofte. Citering af mandsiden:

command | getline [var] 

Kør kommando, der rør output ud i $ 0 eller var,

kommando ville være et bash-script, der indeholder funktionsdefinitionen og udfører funktionen.

Svar

Konvertering fra decimal til hexadecimal er noget, som awk meget godt kan gøre selv. Og du kunne definere en awk -funktion til at gøre det:

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

Nu for at besvare spørgsmålet i almindelighed, skal awk køre bash funktioner, du “behøver awk for at udføre en bash shell, der bash at fortolke definitionen af denne funktion og kalde den funktion med værdien ekstraheret af awk videregivet som argumenter.

Ikke trivielt.

bash understøtter eksportfunktioner via miljøet, så det er tilgængeligt i efterfølgende påkald af bash, så det er en måde at passere definitionen af funktionen til bash påkrævet af awk:

export -f d2h 

De eneste måder for awk at udføre en kommando (bash h ere) er med dens system("cmd") eller print... | "cmd" eller "cmd" | getline. I alle tilfælde kører awk en shell for at fortolke, at cmd, men det vil være sh, ikke bash. Så du skal konstruere en kommandolinje til sh som er en bash påkaldelse, der fortolker en bash kommandolinje for at påkalde funktionen, så du skal være forsigtig med at citere:

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å output fra funktionen tilbage til awk, skal du overføre det tilbage via et rør. Til det skal du bruge cmd | getline i stedet for system(cmd) (som efterlader cmd” s stdout uberørt).

cmd | getline line gemmer en linje (strengt taget en post , poster er som standard linjer), så for at få hele output i de tilfælde, hvor det er lavet af flere linjer, har du brug for en løkke 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 at køre en sh og en bash for hver hver påkaldelse af funktionen , så vil være ret ineffektivt. Det ville ende med at blive endnu mere ineffektivt end at have bash til at læse og opdele med en while read loop:

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

Bemærk også, at siden shellshock eksporterer bash nu funktioner i miljøvariabler, der hedder BASH_FUNC_d2h%%. Nogle sh implementeringer inklusive mksh og nyere versioner af dash fjern disse miljøvariabler 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 at stole på eksportfunktionen for spinkelfunktion, kan du passere funktionsdefinitionen på en anden måde. Det kunne ske via en miljøvariabel med et almindeligt navn:

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

Svar

Prøv at gøre dette:

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

AFAIK, du kan ikke bruge en bash funktion i awk, men kun et script. Du kan bruge en awk-funktion, hvis det er nødvendigt.

Svar

Brug af en brugerdefineret bash-funktion inside awk

Ansvarsfraskrivelse: Jeg er klar over, at dette ikke er, hvad OP forsøger at gøre, men Google vil føre andre som mig til dette svar.

Situation

Du har et bash script, der er organiseret med funktioner (fordi du ikke hader dig selv eller [de fleste] kolleger), og mindst 1 af disse funktioner skal ring til en anden indefra awk.

Løsning

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 

Output

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

Kommentarer

  • jeg repræsentere Queens, hun blev opvokset i Brooklyn

Svar

Bare et hurtigt eksempel for at demonstrere @HaukeLaging “s command|getline:

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

hvor vi følger shell-syntaks i input,

`command` 

bruges til at betegne inline-kommandoer der skal erstattes af resultatet af hans henrettelse.

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

Svar

Dette giver dig gode chancer.

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

systemfunktion giver dig mulighed for at analysere bash-kommandoen inden for awk stream.

Svar

Dette svar, hvis det gælder GNU awk, men andre versioner kan implementere det samme trick. Tanken er, at så længe den højre side af røret er nøjagtig den samme og ikke kommer ud, lukker awk ikke røret. Men hvis det er anderledes, åbner det et nyt rør. Hvis du f.eks. 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å det forventede (?) svar 5: ingen ny skal blev skabt. Men i END-sektionen skrev jeg et ekstra mellemrum, så rørets RHS er anderledes, og en ny skal er skabt uden værdi for variabel a (og vil således udskrive en tom linje ved udgangen. Jeg kan ikke finde den i dokumentation, men den anbefalede måde at bruge langsigtede rør på er som jeg har: læg kommandoen i BEGIN-sektionen i en variabel, og genbrug den variabel.

At give en brugerdefineret funktion til en bash-session og derefter genbrug er det en øvelse, der er overladt til læseren 🙂

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *