Chciałbym zwrócić ciąg z funkcji Bash.
Napiszę przykład w języku Java, aby pokazać, co chciałbym zrobić:
public String getSomeString() {
return "tadaa";
}
String variable = getSomeString();
Poniższy przykład działa w trybie bash, ale czy jest na to lepszy sposób?
function getSomeString {
echo "tadaa"
}
VARIABLE=$(getSomeString)
string
bash
function
return-value
Tomas F.
źródło
źródło
function funcName {
Nawiasem mówiąc , jest to starsza składnia sprzed POSIX odziedziczona po wczesnym ksh (gdzie miała różnice semantyczne, których bash nie honoruje).funcName() {
,function
zamiast, należy użyć zamiast; patrz wiki.bash-hackers.org/scripting/obsoletefunction myFunction { blah; }
jest w porządku; tofunction myFunction() { blah }
nie jest w porządku, tzn. użycie nawiasu z funkcją słowa kluczowego.Odpowiedzi:
Nie ma lepszego sposobu, o którym wiem. Bash zna tylko kody statusu (liczby całkowite) i łańcuchy zapisane na standardowym wyjściu.
źródło
somefunction && echo 'success'
). Jeśli myślisz o funkcji jak o innym poleceniu, ma to sens; polecenia nie „zwracają” niczego przy wyjściu poza kodem stanu, ale w międzyczasie mogą wyświetlać rzeczy, które można przechwycić.Możesz sprawić, aby funkcja wzięła zmienną jako pierwszy argument i zmodyfikowała zmienną za pomocą łańcucha, który chcesz zwrócić.
Drukuje „foo bar rab oof”.
Edycja : dodano cytowanie w odpowiednim miejscu, aby umożliwić spację w łańcuchu w celu uwzględnienia komentarza @Luca Borrione.
Edycja : Jako demonstrację zobacz następujący program. Jest to rozwiązanie ogólnego zastosowania: pozwala nawet otrzymać ciąg znaków do zmiennej lokalnej.
To drukuje:
Edycja : wykazanie, że wartość oryginalnej zmiennej jest dostępna w funkcji, co zostało niepoprawnie skrytykowane przez @Xichen Li w komentarzu.
Daje to wynik:
źródło
fgm
odpowiedź napisana w uproszczony sposób? To nie zadziała, jeśli ciągfoo
zawiera białe spacje, podczas gdy tenfgm
będzie .. jak pokazuje.\$$1
. Jeśli szukasz czegoś innego, daj mi znać.printf '%q' "$var"
. % q jest łańcuchem formatu dla ucieczki powłoki. Następnie przekaż to na surowo.Wszystkie powyższe odpowiedzi ignorują to, co zostało powiedziane na stronie podręcznika bash.
Przykładowy kod
I wyjście
Również w pdksh i ksh ten skrypt robi to samo!
źródło
Bash, od wersji 4.3, lutego 2014 (?), Ma wyraźne wsparcie dla zmiennych referencyjnych lub referencji nazw (namerefs), wykraczających poza „eval”, z tą samą korzystną wydajnością i efektem pośrednim, i które mogą być wyraźniejsze w twoich skryptach, a także trudniejsze aby „zapomnieć o„ ewaluacji ”i naprawić ten błąd”:
i również:
Na przykład ( EDYCJA 2 : (dziękuję Ron) wstawił (przedrostek) nazwę zmiennej wewnętrznej funkcji, aby zminimalizować kolizje zmiennych zewnętrznych, które ostatecznie powinny poprawnie odpowiedzieć, problem podniesiony w komentarzach Karstena):
i testowanie tego przykładu:
Zauważ, że wbudowana bash „deklaruj”, gdy jest używana w funkcji, domyślnie powoduje, że zadeklarowana zmienna jest „lokalna”, a „-n” może być również używane z „lokalną”.
Wolę odróżniać zmienne „ważne deklarować” od „nudnych zmiennych lokalnych”, więc użycie „deklarowania” i „lokalnego” w ten sposób działa jako dokumentacja.
EDYCJA 1 - (Odpowiedź na komentarz poniżej przez Karstena) - Nie mogę już dodawać komentarzy poniżej, ale komentarz Karstena zmusił mnie do myślenia, więc zrobiłem następujący test, który DZIAŁA WYGODNIE, AFAICT - Karsten, jeśli to czytasz, proszę podać dokładny zestaw kroków testowych z wiersza poleceń, pokazując, że istnieje problem, który, jak zakładasz, działa dobrze:
(Uruchomiłem to właśnie teraz, po wklejeniu powyższej funkcji do terminu bash - jak widać, wynik działa dobrze).
źródło
declare
w funkcjach tworzone są zmienne lokalne (ta informacja nie jest podawana przezhelp declare
): „... Gdy są używane w funkcji, deklaruj i składaj, aby każda nazwa była lokalna, tak jak w przypadku polecenia lokalnego, chyba że - dostarczona jest opcja g ... "K=$1; V=$2; eval "$A='$V'";
, ale jeden błąd (np. Parametr pusty lub pominięty) i byłoby to bardziej niebezpieczne. @zenaan problem podniesiony przez @Karsten ma zastosowanie, jeśli wybierzesz „wiadomość” jako nazwę zmiennej zwracanej zamiast „ret”.Podobnie jak powyżej bstpierre , używam i polecam użycie jawnych nazw zmiennych wyjściowych:
Zwróć uwagę na użycie cytowania $. Pozwoli to uniknąć interpretacji treści
$result
jako znaków specjalnych powłoki. Przekonałem się, że jest to rząd wielkości szybszy niżresult=$(some_func "arg1")
idiom przechwytywania echa. Różnica prędkości wydaje się jeszcze bardziej zauważalna przy użyciu bash na MSYS, gdzie przechwytywanie standardowe z wywołań funkcji jest niemal katastrofalne.W porządku jest wysyłanie zmiennych lokalnych, ponieważ locals są dynamicznie skalowane w bash:
źródło
echo
wnętrza funkcji w połączeniu z zastępowaniem poleceń!Możesz także przechwycić dane wyjściowe funkcji:
Wygląda dziwnie, ale jest lepszy niż użycie zmiennych globalnych IMHO. Przekazywanie parametrów działa jak zwykle, wystarczy umieścić je w nawiasach klamrowych lub tylnych.
źródło
Najprostszym i najbardziej niezawodnym rozwiązaniem jest stosowanie podstawiania poleceń, jak napisali inni ludzie:
Minusem jest wydajność, ponieważ wymaga to osobnego procesu.
Inna technika sugerowana w tym temacie, mianowicie przekazanie nazwy zmiennej do przypisania jako argumentu, ma skutki uboczne i nie poleciłbym jej w swojej podstawowej formie. Problem polega na tym, że prawdopodobnie będziesz potrzebować niektórych zmiennych w funkcji do obliczenia wartości zwracanej, i może się zdarzyć, że nazwa zmiennej przeznaczonej do przechowywania wartości zwracanej będzie zakłócać jedną z nich:
Oczywiście nie możesz zadeklarować wewnętrznych zmiennych funkcji jako lokalnych, ale naprawdę powinieneś to zawsze robić, ponieważ w przeciwnym razie możesz przypadkowo zastąpić zmienną niepowiązaną z zakresu nadrzędnego, jeśli istnieje taka o tej samej nazwie .
Jednym z możliwych obejść jest jawne zadeklarowanie przekazywanej zmiennej jako globalnej:
Jeśli nazwa „x” zostanie przekazana jako argument, drugi wiersz treści funkcji zastąpi poprzednią deklarację lokalną. Ale same nazwy mogą nadal przeszkadzać, więc jeśli zamierzasz użyć wartości poprzednio zapisanej w przekazanej zmiennej przed zapisaniem wartości zwracanej, pamiętaj, że musisz skopiować ją do innej zmiennej lokalnej na samym początku; w przeciwnym razie wynik będzie nieprzewidywalny! Poza tym będzie to działać tylko w najnowszej wersji BASH, a mianowicie 4.2. Bardziej przenośny kod może wykorzystywać jawne konstrukcje warunkowe z tym samym efektem:
Być może najbardziej eleganckim rozwiązaniem jest zarezerwowanie jednej globalnej nazwy dla zwracanych wartości funkcji i konsekwentne stosowanie jej w każdej pisanej funkcji.
źródło
eval
ideclare -n
. Obejście polegające na posiadaniu pojedynczej dedykowanej nazwy zmiennej, takiej jakresult
dla wszystkich parametrów wyjściowych, wydaje się jedynym rozwiązaniem, które nie wymaga, aby funkcje znały wszystkich swoich rozmówców, aby uniknąć konfliktów.Jak wspomniano wcześniej, „poprawnym” sposobem na zwrócenie ciągu znaków z funkcji jest zastąpienie polecenia. W przypadku, gdy funkcja musi również wyświetlać dane na konsoli (jak wspomniano powyżej @Mani), utwórz tymczasowy plik fd na początku funkcji i przekieruj do konsoli. Zamknij tymczasowy fd przed zwróceniem łańcucha.
wykonywanie skryptu bez parametrów powoduje ...
mam nadzieję, że to pomaga ludziom
-Andy
źródło
3>&1
na początku skryptu, a następnie manipulując&1
&3
i innym symbolem zastępczym&4
w ramach funkcji. Jednak brzydkie.Możesz użyć zmiennej globalnej:
To daje
źródło
Aby zilustrować mój komentarz do odpowiedzi Andy'ego, z dodatkową manipulacją deskryptorem pliku, aby uniknąć użycia
/dev/tty
:Wciąż jednak paskudne.
źródło
Sposób, w jaki to masz, jest jedynym sposobem na zrobienie tego bez naruszania zakresu. Bash nie ma pojęcia typów zwracanych, po prostu kody wyjściowe i deskryptory plików (stdin / out / err itp.)
źródło
Zwracając się do głowy Vicky Ronnen , biorąc pod uwagę następujący kod:
da
Może normalnym scenariuszem jest użycie składni użytej w
test_inside_a_func
funkcji, dlatego w większości przypadków można użyć obu metod, chociaż przechwytywanie danych wyjściowych jest bezpieczniejszą metodą zawsze działającą w każdej sytuacji, naśladując zwracaną wartość z funkcji, którą można znaleźć w innych językach, jakVicky Ronnen
prawidłowo wskazano.źródło
Myślę, że wszystkie opcje zostały wyliczone. Wybór jednego może sprowadzać się do kwestii najlepszego stylu dla konkretnej aplikacji iw tym duchu chcę zaoferować jeden konkretny styl, który uważam za użyteczny. W bash zmienne i funkcje nie są w tej samej przestrzeni nazw. Tak więc traktowanie zmiennej o tej samej nazwie jako wartości funkcji jest konwencją, która według mnie minimalizuje konflikty nazw i poprawia czytelność, jeśli zastosuję ją rygorystycznie. Przykład z prawdziwego życia:
I przykład użycia takich funkcji:
Jak widać, status zwrotu jest dostępny do użycia, gdy jest to potrzebne, lub zignoruj, jeśli nie potrzebujesz. Zmienną „zwróconą” można również użyć lub zignorować, ale oczywiście dopiero po wywołaniu funkcji.
Oczywiście to tylko konwencja. Możesz powrócić do ustawienia powiązanej wartości przed jej zwróceniem (stąd moja konwencja zawsze zerowania jej na początku funkcji) lub podeptania jej wartości poprzez ponowne wywołanie funkcji (być może pośrednio). Mimo to jest to konwencja, która uważam za bardzo przydatną, jeśli często używam funkcji bash.
W przeciwieństwie do sentymentu, że jest to znak, który należy np. „Przejść do perla”, moja filozofia jest taka, że konwencje są zawsze ważne dla zarządzania złożonością dowolnego języka.
źródło
Możesz
echo
napisać, ale złap go poprzez potokowanie (|
) funkcji do czegoś innego.Możesz to zrobić za pomocą
expr
, choć ShellCheck zgłasza to użycie jako przestarzałe.źródło
myfunc | read OUTPUT ; echo $OUTPUT
nic nie daje.myfunc | ( read OUTPUT; echo $OUTPUT )
otrzymuje oczekiwaną wartość i wyjaśnia, co dzieje się po prawej stronie. Ale oczywiście WYJŚCIE nie jest wtedy dostępne tam, gdzie jest potrzebne ...Kluczowym problemem każdego schematu „nazwanej zmiennej wyjściowej”, w którym osoba wywołująca może przekazać nazwę zmiennej (bez względu na to, czy używa
eval
lubdeclare -n
), jest nieumyślne aliasing, tzn. Kolizje nazw: Z punktu widzenia enkapsulacji okropne jest to, że nie można dodać ani zmienić nazwy zmienna lokalna w funkcji bez sprawdzania najpierw WSZYSTKICH wywołujących funkcję, aby upewnić się, że nie chcą przekazać tej samej nazwy co parametr wyjściowy. (Lub w innym kierunku, nie chcę czytać źródła funkcji, którą wywołuję, aby upewnić się, że parametr wyjściowy, którego zamierzam użyć, nie jest lokalny w tej funkcji).Jedynym sposobem na to jest użycie pojedynczej dedykowanej zmiennej wyjściowej, takiej jak
REPLY
(jak sugeruje Evi1M4chine ) lub konwencji podobnej do tej zaproponowanej przez Rona Burka .Jednak możliwe jest, aby funkcje korzystały wewnętrznie ze stałej zmiennej wyjściowej , a następnie dodać trochę cukru na wierzchu, aby ukryć ten fakt przed rozmówcą , jak robiłem z
call
funkcji w poniższym przykładzie. Uważaj to za dowód koncepcji, ale najważniejsze sąREPLY
i może również zwracać kod wyjścia, jak zwykleREPLY
(patrzwrapper
przykład). Kod wyjścia funkcji przechodzi przez, więc używanie ich w npif
lubwhile
lub podobne konstrukty działa zgodnie z oczekiwaniami.Powodem tego jest to, że
call
sama funkcja nie ma lokalnych i nie używa żadnych zmiennych innych niżREPLY
, unikając potencjalnego konfliktu nazw. W miejscu, w którym przypisywana jest nazwa zmiennej wyjściowej zdefiniowanej przez program wywołujący, efektywnie znajdujemy się w zakresie programu wywołującego (technicznie w identycznym zakresiecall
funkcji), a nie w zakresie wywoływanej funkcji.Wynik:
źródło
wzorzec bash, aby zwrócić zarówno obiekty wartości skalarnych, jak i tablicowych :
definicja
wezwanie
źródło
W moich programach, zgodnie z konwencją, właśnie do tego służy wcześniej istniejąca
$REPLY
zmienna, któraread
używa tego właśnie celu.To
echo
esAle aby uniknąć konfliktów, wystarczy każda inna zmienna globalna.
Jeśli to nie wystarczy, polecam rozwiązanie Markarian451 .
źródło
źródło