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
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
:
- 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.
- Możemy rozszerzyć wewnętrzną komendę powłoki o:
#!/usr/bin/gawk -f BEGIN { FS ="`"; } NF==3 { $2 | getline $2 } { print }
- 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 🙂
d2h
był plikiem wykonywalnym, ale nie, jeśli ' sa ” funkcja zdefiniowana w .bashrc lub w skrypcie powłoki „.source
), ale mogę ' f figure wyjaśnij, dlaczego nie ' nie działa z.bashrc
.$2
jest w końcu interpretowana jako kod powłoki.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") }'