De bash-shellfunctie gebruiken binnen AWK

Is het mogelijk om de bash-functie op de een of andere manier binnen AWK te gebruiken?

Voorbeeldbestand (string, int, int , int)

Mike 247808 247809 247810 

Probeert waarden om te zetten van decimaal naar hexadecimaal.

Functie gedefinieerd in .bashrc of in shell-script.

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

Antwoord

Probeer gebruik system() functie:

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

In jouw geval zal system bel d2h 247808 en voeg de uitvoer van deze opdracht toe aan de printf uitvoer:

Mike 3C800 

BEWERK:

Omdat system sh gebruikt in plaats van bash Ik kan “geen manier vinden om toegang te krijgen tot .bashrc. Maar je kunt nog steeds functies van je huidige bash-script gebruiken:

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

Ik weet het niet waarom, maar dit werkt niet op mijn Ubuntu 16.04. Dit is vreemd, omdat het vroeger werkte op Ubuntu 14.04.

Reacties

  • Dit zou kunnen werken als d2h waren een uitvoerbaar bestand, maar niet als het ‘ sa ” -functie gedefinieerd is in .bashrc of in shell-script “.
  • @MichaelHomer ja, je hebt gelijk. Dankzij uw opmerking realiseerde ik me dat ik de vraag die niemand stelde heb beantwoord. Maar ik heb een manier gevonden om functies uit het huidige script (en mogelijk uit andere scripts via source) te gebruiken, maar ik kan ‘ figuur uit waarom het niet ‘ werkt met .bashrc.
  • Dat ‘ is ook een kwetsbaarheid voor commando-injectie, aangezien de inhoud van $2 uiteindelijk wordt geïnterpreteerd als shellcode.
  • If export -f d2h werkt niet ‘ t werk, je kunt het doen zoals voor ksh / zsh: (\ 42 en \ 47 zijn de escape-codes voor eenvoudige en dubbele aanhalingstekens) CODE=$(typeset -f d2h) awk ' { system("bash -c \47eval \42$CODE\42; d2h \42"$2"\42\47") }'

Answer

Je kunt bash aanroepen vanuit awk en de uitvoer ervan gebruiken. Dat is natuurlijk gevaarlijk vanuit een prestatieperspectief als het te vaak gebeurt. Onder vermelding van de man-pagina:

command | getline [var] 

Voer het commando uit om de uitvoer ofwel naar $ 0 of var te leiden,

commando zou een bash-script zijn dat de functiedefinitie bevat en de functie uitvoert.

Antwoord

Omzetten van decimaal naar hexadecimaal is iets dat awk zelf heel goed kan. En je zou een awk -functie kunnen definiëren om het te doen:

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

Om de vraag nu in het algemene geval te beantwoorden: awk wordt uitgevoerd bash functies, je “d hebt awk nodig om een bash shell uit te voeren, die bash om de definitie van die functie te interpreteren, en die functie aan te roepen, met de waarde geëxtraheerd door awk doorgegeven als argumenten.

Niet triviaal.

bash ondersteunt het exporteren van functies via de omgeving, dus het is beschikbaar in volgende aanroepen van bash, dus dat is een manier om de definitie van de functie voor de bash aangeroepen door awk:

export -f d2h 

De enige manieren waarop awk een commando kan uitvoeren (bash h ere) zijn met zijn system("cmd"), of print... | "cmd" of "cmd" | getline. In alle gevallen voert awk een shell uit om dat cmd te interpreteren, maar het zal sh, niet bash. U moet dus een opdrachtregel maken voor sh die een bash aanroep is die een bash opdrachtregel om de functie aan te roepen, dus je moet voorzichtig zijn met aanhalingstekens:

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

Als je de uitvoer van de functie terug wilt krijgen in awk, je “zou het terug moeten sturen via een pipe. Daarvoor zou je” cmd | getline gebruiken in plaats van system(cmd) (waardoor cmd” s stdout onaangeroerd blijft).

cmd | getline line slaat één regel op (strikt genomen één record , records zijn standaard regels), dus om de hele uitvoer te krijgen in de gevallen waarin het “uit meerdere regels bestaat,” heb je een lus nodig zoals:

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

Dat betekent dat er één sh en één bash moet worden uitgevoerd voor elke aanroep van de functie , dus het zal behoorlijk inefficiënt zijn. Dat zou uiteindelijk zelfs aanzienlijk inefficiënter zijn dan bash het lezen en splitsen te laten doen met een while read loop:

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

Merk ook op dat sinds shellshock bash nu functies exporteert in omgevingsvariabelen die de naam BASH_FUNC_d2h%%. Sommige sh implementaties, waaronder mksh en nieuwere versies van dash verwijderen die omgevingsvariabelen uit de omgeving:

$ 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 

Dus in plaats van te vertrouwen op de flimsy functie export functie, zou je de functiedefinitie op een andere manier kunnen doorgeven. Het kan zijn via een omgevingsvariabele met een gebruikelijke naam:

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

Answer

Probeer dit eens:

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

AFAIK, je kunt “geen bash gebruiken functie in awk maar alleen een script. U kunt indien nodig een awk-functie gebruiken.

Answer

Een door de gebruiker gedefinieerde bash-functie gebruiken inside awk

Disclaimer: Ik realiseer me dat dit niet is wat het OP probeert te doen, maar Google zal anderen zoals ik naar dit antwoord leiden.

Situatie

Je hebt een bash script dat is georganiseerd met functies (omdat je jezelf of [de meeste] collegas niet haat) en ten minste 1 van die functies moet bel een ander vanuit awk.

Oplossing

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 

Uitvoer

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

Reacties

  • ik vertegenwoordigen Queens, ze groeide op in Brooklyn

Answer

Even een kort voorbeeld om @HaukeLaging “s te demonstreren command|getline:

  1. laat invoer zijn:
Dear friends my name is `id -nu` and today is `date "+%Y-%m-%d"`. 

waar we de shell-syntaxis volgen, wordt in de invoer

`command` 

gebruikt om inline-opdrachten aan te duiden die moeten worden vervangen door het resultaat van zijn uitvoering.

  1. We kunnen het inline shell-commando uitbreiden met:
#!/usr/bin/gawk -f BEGIN { FS ="`"; } NF==3 { $2 | getline $2 } { print } 
  1. Gebruik (na de gebruikelijke chmod):
$ expand-inline input Dear friends my name is jjoao and today is 2018-01-15. 

Antwoord

Dit geeft je goede kansen.

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

Met de systeemfunctie kun je het bash-commando in een awk-stream parseren.

Antwoord

Dit antwoord is voor GNU awk, maar andere versies kunnen dezelfde truc implementeren. Het idee is dat zolang de rechterkant van de buis precies hetzelfde is en niet naar buiten komt, awk de buis niet sluit. Maar als het anders is, opent het een nieuwe pijp. Als u bijvoorbeeld typt:

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

, kunt u typen:

a=5 echo $a 

en krijg het verwachte (?) antwoord 5: er is geen nieuwe shell uitgezet. Maar in de END-sectie typte ik een extra spatie, dus de RHS van de pijp is anders, en een nieuwe shell wordt voortgebracht zonder waarde voor variabele a (en zal dus een lege regel afdrukken bij het verlaten. Ik kan het niet vinden in de documentatie, maar de aanbevolen manier om pijpen voor de lange termijn te gebruiken is zoals ik heb gedaan: plaats het commando in de BEGIN-sectie in een variabele, en hergebruik die variabele.

Een door de gebruiker gedefinieerde functie geven aan een bash-sessie en dan is hergebruik een oefening die aan de lezer wordt overgelaten 🙂

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *