Używanie funkcji powłoki bash wewnątrz AWK

Czy można w jakiś sposób użyć funkcji bash wewnątrz AWK?

Przykładowy plik (string, int, int , int)

Mike 247808 247809 247810 

Próba konwersji wartości z dziesiętnych na szesnastkowe.

Funkcja zdefiniowana w .bashrc lub w skrypcie powłoki.

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

Odpowiedź

Spróbuj użyj funkcji system():

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

W Twoim przypadku system wywołaj d2h 247808, a następnie dołącz wyjście tego polecenia do printf wyjścia:

Mike 3C800 

EDYTUJ:

Jako system używa sh zamiast bash Nie mogę „znaleźć sposobu, aby uzyskać dostęp do .bashrc. Ale nadal możesz używać funkcji z bieżącego skryptu 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 

EDYCJA 2:

Nie wiem dlaczego, ale to nie działa na moim Ubuntu 16.04. To dziwne, ponieważ kiedyś działało na Ubuntu 14.04.

Komentarze

  • To mogłoby działać, gdyby d2h był plikiem wykonywalnym, ale nie, jeśli ' sa ” funkcja zdefiniowana w .bashrc lub w skrypcie powłoki „.
  • @MichaelHomer tak, masz rację. Dzięki waszemu komentarzowi zdałem sobie sprawę, że odpowiedziałem na pytanie, którego nikt nie zadał. Ale znalazłem sposób na użycie funkcji z bieżącego skryptu (i prawdopodobnie z innych skryptów za pośrednictwem source), ale mogę ' f figure wyjaśnij, dlaczego nie ' nie działa z .bashrc.
  • To ' jest również luką umożliwiającą wstrzykiwanie poleceń, ponieważ treść $2 jest w końcu interpretowana jako kod powłoki.
  • If export -f d2h nie ' t działa, możesz to zrobić tak jak dla ksh / zsh: (\ 42 i \ 47 to kody ucieczki dla prostych i podwójnych cudzysłowów) CODE=$(typeset -f d2h) awk ' { system("bash -c \47eval \42$CODE\42; d2h \42"$2"\42\47") }'

Odpowiedź

Możesz wywołać bash z awk i użyć jego wyjścia. Jest to oczywiście niebezpieczne z punktu widzenia wydajności, jeśli zdarza się to zbyt często. Cytując stronę podręcznika:

command | getline [var] 

Uruchom polecenie, przesyłając dane wyjściowe do $ 0 lub var,

polecenie byłoby skryptem basha zawierającym definicję funkcji i wykonującym funkcję.

Odpowiedź

Konwersja z dziesiętnego na szesnastkowy to coś, co awk może zrobić samo. Możesz też zdefiniować funkcję awk , aby to zrobić:

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

Teraz, aby odpowiedzieć na pytanie w ogólnym przypadku, aby awk uruchomić bash, potrzebowałbyś awk do wykonania powłoki bash, która bash aby zinterpretować definicję tej funkcji i wywołać tę funkcję z wartością wyodrębnioną przez awk przekazaną jako argumenty.

Nie jest to trywialne.

bash obsługuje eksportowanie funkcji przez środowisko, więc jest dostępny w kolejnych wywołaniach bash, więc jest to jeden sposób definicja funkcji w bash wywołana przez awk:

export -f d2h 

Jedyne sposoby, w jakie awk może wykonać polecenie (bash h ere) są z system("cmd") lub print... | "cmd" lub "cmd" | getline. We wszystkich przypadkach awk uruchamia powłokę, aby zinterpretować to cmd, ale będzie to sh, a nie bash. Musisz więc utworzyć wiersz poleceń dla sh, czyli wywołania bash, które interpretuje bash wiersz poleceń, aby wywołać funkcję, więc musisz uważać z cytowaniem:

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

Jeśli chcesz otrzymać wynik funkcji z powrotem do awk, „d musiałbyś przesłać go z powrotem potokiem. W tym celu” użyjesz cmd | getline zamiast system(cmd) (co pozostawia cmd” standardowe wyjście nietknięte).

cmd | getline line przechowuje jedną linię (ściśle mówiąc jeden rekord , rekordy są domyślnie wierszami), więc aby otrzymać cały wynik w przypadkach, gdy składa się on z kilku wierszy, potrzebujesz pętli takiej jak:

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

To oznacza uruchamianie jednego sh i jednego bash dla każdego wywołania funkcji , więc będzie dość nieefektywne. Byłoby to nawet znacznie bardziej nieefektywne niż bash wykonanie odczytu i podzielenia za pomocą while read loop:

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

Należy również pamiętać, że od czasu shellshock bash eksportuje teraz funkcje w zmiennych środowiskowych o nazwach takich jak BASH_FUNC_d2h%%. Niektóre sh implementacje, w tym mksh i nowsze wersje dash usuń te zmienne środowiskowe ze środowiska:

$ 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 

Więc zamiast polegać na kruchej funkcji eksportu funkcji, możesz przekazać definicję funkcji w inny sposób. Może to być za pośrednictwem zmiennej środowiskowej o zwykłej nazwie:

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

Odpowiedź

Spróbuj to zrobić:

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

AFAIK, nie możesz używać basha funkcja awk, ale tylko skrypt. W razie potrzeby możesz użyć funkcji awk.

Odpowiedź

Używanie funkcji bash zdefiniowanej przez użytkownika inside awk

Zastrzeżenie: zdaję sobie sprawę, że nie to próbuje zrobić OP, ale Google poprowadzi innych takich jak ja do tej odpowiedzi.

Sytuacja

Masz skrypt bash, który zawiera funkcje (ponieważ nie nienawidzisz siebie ani [większości] współpracowników) i co najmniej jedna z tych funkcji musi zadzwoń do innego z poziomu awk.

Rozwiązanie

Skrypt

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

Wyjście

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

Komentarze

  • I reprezentuje Queens, wychowała się na Brooklynie

Odpowiedź

Tylko krótki przykład, aby zademonstrować @HaukeLaging „s command|getline:

  1. niech dane wejściowe będą:
Dear friends my name is `id -nu` and today is `date "+%Y-%m-%d"`. 

gdzie postępujemy według składni powłoki, na wejściu

`command` 

jest używany do oznaczenia poleceń wbudowanych , które mają być zastąpione wynikiem jego wykonanie.

  1. Możemy rozszerzyć wewnętrzną komendę powłoki o:
#!/usr/bin/gawk -f BEGIN { FS ="`"; } NF==3 { $2 | getline $2 } { print } 
  1. Użycie (po zwykłym chmod):
$ expand-inline input Dear friends my name is jjoao and today is 2018-01-15. 

Odpowiedź

To da ci duże szanse.

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

Funkcja systemowa umożliwia analizowanie polecenia bash w strumieniu awk.

Odpowiedź

Ta odpowiedź dotyczy GNU awk, ale inne wersje mogą implementować tę samą sztuczkę. Chodzi o to, że dopóki prawa strona potoku jest dokładnie taka sama i nie wychodzi, awk nie zamyka potoku. Ale jeśli jest inny, otworzy nową rurę. Na przykład, jeśli wpiszesz:

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

, możesz wpisać:

a=5 echo $a 

i uzyskać oczekiwaną (?) odpowiedź 5: nie pojawiła się nowa powłoka. Ale w sekcji END wpisałem dodatkową spację, więc RHS potoku jest inny, i pojawia się nowa powłoka bez wartości dla zmiennej a (i dlatego wypisze pustą linię przy wyjściu. Nie mogę jej znaleźć w ale zalecany sposób używania potoków długoterminowych jest taki, jak ja: umieść polecenie w sekcji BEGIN w zmiennej i ponownie użyj tej zmiennej.

Nadanie funkcji zdefiniowanej przez użytkownika sesji bash i ponowne użycie jest ćwiczeniem pozostawionym czytelnikowi 🙂

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *