Alias ​​scoping w funkcjach bash

9

Używam skryptu (do którego nie mam uprawnień do zapisu), który tworzy kilka aliasów do skonfigurowania środowiska. Chciałbym utworzyć funkcję bash, aby skonfigurować środowisko, ale wygląda na to, że aliasy nie przetrwają w ciele funkcji.

Oto minimalny przykład:

# aliases.sh
alias fooAlias='echo "this will never work!"'  

.

# .bashrc
function setupLotsOfThings() {
    source aliases.sh
    fooAlias
}

.

Teraz, jeśli po prostu źródle aliases.shinteraktywnie, rzeczy działają zgodnie z oczekiwaniami:

[mycomputer]~/ $ source aliases.sh
[mycomputer]~/ $ fooAlias
this will never work!

Jednak jeśli wywołam funkcję zdefiniowaną w moim .bashrc, nie rozpozna aliasu po uzyskaniu jego definicji:

[mycomputer]~/ $ setupLotsOfThings
-bash: fooAlias: command not found

Co tu się dzieje? Czy brakuje mi czegoś w zakresie aliaspolecenia, gdy jest ono używane w funkcji?

Edycja: Dodam kilka szczegółów poza minimalny przykład, aby rzucić nieco światła na to, co próbuję osiągnąć.

Do mojej pracy opracowuję i uruchamiam wiele programów w klastrze i / lub sieci. Mam kilka projektów, które wymagają zupełnie różnych środowisk, takich jak różne wersje gcc, określone wydania oprogramowania, ścieżki konfiguracji i danych oraz różne zmienne środowiskowe. Administratorzy zapewniają skrypty do konfigurowania różnych rzeczy, zwykle poprzez zdefiniowanie funkcji powłoki lub aliasów, które wywołują inne funkcje lub aliasy lub uruchamiają różne skrypty. Dla mnie to czarna skrzynka.

Chciałbym skonfigurować własne środowiska za pomocą jednego polecenia. Obecnie robię coś takiego:

[mycomputer]~/ $ source /some/environment/setup/script.sh
[mycomputer]~/ $ aliasToSetupSomeSoftwareVersion    #this was defined in the above
[mycomputer]~/ $ anotherAliasForOtherSoftware
[mycomputer]~/ $ source /maybe/theres/another/script.sh
[mycomputer]~/ $ runSomeOtherSetup      # this was defined in the new script

Te komendy na ogół muszą być uruchamiane w kolejności. Moim pomysłem było po prostu skopiowanie powyższych linii do bloku funkcyjnego, ale jak pokazuje oryginalny przykład, to po prostu nie działa. Alternatywne obejścia są mile widziane!

gonić
źródło

Odpowiedzi:

10

Alternatywnym rozwiązaniem jest wklejenie tych poleceń do pliku tekstowego zamiast bloku funkcyjnego. Coś jak:

## This is needed to make the sourced aliases available
## within the script.
shopt -s expand_aliases

source /some/environment/setup/script.sh
aliasToSetupSomeSoftwareVersion
anotherAliasForOtherSoftware
source /maybe/theres/another/script.sh
runSomeOtherSetup

Zapisz to tak setup1.sh, jak chcesz. Sztuczka polega na tym, aby następnie pobrać ten plik, a nie wykonać go:

$ source setup1.sh

Spowoduje to uruchomienie aliasów znajdujących się w skrypcie i udostępnienie ich bieżącej powłoce.

Możesz dodatkowo uprościć proces, dodając to do .bashrc:

alias setupLotsOfThings="source setup1.sh"

Teraz możesz po prostu uruchomić setupLotsOfThingsi uzyskać pożądane zachowanie z funkcji.


Wyjaśnienie

Istnieją tutaj dwa problemy. Po pierwsze, aliasy nie są dostępne dla funkcji, w której zostały zadeklarowane, ale tylko po wyjściu z tej funkcji, a po drugie, że aliasy nie są dostępne w skryptach. Oba są wyjaśnione w tej samej sekcji man bash:

Aliasy nie są rozwijane, gdy powłoka nie jest interaktywna, chyba że ustawiono opcję powłoki expand_aliases przy użyciu shopt (patrz opis shopt w POLECENIA WBUDOWANE POWŁOKI poniżej).

Zasady dotyczące definiowania i używania aliasów są nieco mylące. Bash zawsze czyta co najmniej jeden pełny wiersz danych wejściowych przed wykonaniem dowolnego polecenia w tym wierszu. Aliasy są rozszerzane po odczytaniu polecenia, a nie podczas jego wykonywania. Dlatego definicja aliasu pojawiająca się w tym samym wierszu, co inne polecenie, nie obowiązuje, dopóki nie zostanie odczytany następny wiersz danych wejściowych. Nowy alias nie wpływa na polecenia następujące po definicji aliasu w tym wierszu. To zachowanie jest również problemem podczas wykonywania funkcji. Aliasy są rozszerzane, gdy odczytywana jest definicja funkcji, a nie podczas wykonywania funkcji, ponieważ sama definicja funkcji jest poleceniem złożonym. W związku z tym aliasy zdefiniowane w funkcji są
dostępne dopiero po wykonaniu tej funkcji.
Dla bezpieczeństwa zawsze umieszczaj definicje aliasów w osobnym wierszu i nie używaj aliasu w poleceniach złożonych.

Następnie istnieje różnica między wykonywaniem a pozyskiwaniem pliku. Zasadniczo uruchomienie skryptu powoduje, że działa on w osobnej powłoce, a podczas pozyskiwania powoduje, że działa w bieżącej powłoce. Sourcing setup.shudostępnia aliasy powłoce nadrzędnej podczas wykonywania jej tak, jak skrypt nie.

terdon
źródło
Cóż, pracuję nad klastrem i mam kilka takich skryptów „aliasingowych”, które pomagają konfigurować środowiska programowe itp. Skrypt aliasowy definiuje wiele różnych aliasów; moim celem jest wywołanie określonego środowiska poprzez wyszukanie poprawnych aliasów, a następnie uruchomienie (niektórych) tych aliasów. Wolę to zrobić za pomocą jednego polecenia, niż wykonywać kilka poleceń po kolei.
gonić
A PS skrypt, który konfiguruje aliasy, jest niestety bardzo szczegółowy i nieco powolny (ponieważ dotyka wielu plików na NFS), więc wolę nie źródlać tych wszystkich rzeczy, np. Podczas logowania.
gonić
@chase, ale są one pozyskiwane tylko podczas uruchamiania setupLotsOfThings, po prostu nie są dostępne dla samej funkcji. Działają w powłoce, z której wywołałeś funkcję. W każdym razie, jeśli twoja funkcja pozyskuje tylko aliasy, dlaczego po prostu nie użyć aliasu? Na przykład: alias setupstuff="source aliases.sh".
terdon
W porządku, ale nie martwię zakresie jednej se . Idealnie chciałbym po prostu połączyć „elementy źródłowe” i krok „uruchom aliasy” w jeden. Być może można to zrobić za pomocą 3 funkcji? function sourceStuff () {source ...}; funkcja runStuff () {someAlias; ...}; funkcja setupLotsOfThings () {sourceStuff; runStuff; };
gonić
@ Chase tak, myślałem o tym, ale to nie działało :). Czy możesz poszerzyć swoje pytanie o coś więcej, co musisz zrobić? Funkcje mogą poradzić sobie tylko z tak dużą złożonością, że zamiast tego może być konieczne napisanie małego skryptu.
terdon
7

W rzeczywistości twoje aliasy są dostępne po załadowaniu funkcji! Możesz użyć ich w interaktywnej powłoce lub .bashrcpo uruchomieniu funkcji.

Ograniczeniem jest to, że aliasy w definicji funkcji są rozszerzane, gdy definicja funkcji jest czytana, a nie podczas oceny funkcji. To ograniczenie bashu. To zadziała:

function setupLotsOfThings() {
    source aliases.sh
}
setupLotsOfThings
fooAlias

Ale nie to:

function setupLotsOfThings() {
    source aliases.sh
}
function useTheAliases() {
    fooAlias
}
setupLotsOfThings
useTheAliases

Jeśli potrzebujesz aliasów, które są użyteczne w funkcjach i które można zdefiniować po sparsowaniu funkcji, ustaw je zamiast nich. Pamiętaj, że możesz użyć commandwbudowanego polecenia, aby wywołać zewnętrzne polecenie z funkcji o tej samej nazwie.

Gilles „SO- przestań być zły”
źródło