Mam te funkcje w ~/.bashrc
:
function guard() {
if [ -e 'Gemfile' ]; then
bundle exec guard "$@"
else
command guard "$@"
fi
}
function rspec() {
if [ -e 'Gemfile' ]; then
bundle exec rspec "$@"
else
command rspec "$@"
fi
}
function rake() {
if [ -e 'Gemfile' ]; then
bundle exec rake "$@"
else
command rake "$@"
fi
}
Jak widzisz, te funkcje są bardzo podobne. Chcę zdefiniować te 3 funkcje jednocześnie. Czy istnieje sposób, aby to zrobić?
środowisko
bash --version
GNU bash, version 3.2.51(1)-release (x86_64-apple-darwin13)
for loop?
oznacza to, że to znaczy, że zmienne zadeklarowane wfor loop
zasadzie znikają - oczekiwałbym tego samego od funkcji z tych samych powodów.bash -c 'for i in 1; do :; done; echo $i'
=>1
. Tetype
wyraźnie pokazuje, że istnieją funkcje poza zakresem pętli.bash
dynamicznym określaniu zakresu wszystko, co możesz uzyskać, tolocal
zmienna lokalna dla zakresu całej funkcji , zmienne zdecydowanie nie „znikają” po pętli. W rzeczywistości, ponieważ nie ma tu żadnej funkcji, zdefiniowanielocal
zmiennej w tym przypadku nie jest nawet możliwe .Powyższa wola,
. source /dev/fd/3
która jest wprowadzana do_gem_dec()
funkcji za każdym razem, gdy jest wywoływana jakohere-document. _gem_dec's
zadanie wstępnie ocenione, ma otrzymać jeden parametr i wstępnie ocenić go zarówno jakobundle exec
cel, jak i nazwę funkcji, w której jest on docelowy.NOTE: . sourcing shell expansions results in twice-evaluated variables - just like eval. It can be risky.
W powyższym przypadku nie sądzę, aby istniało jakiekolwiek ryzyko.
Jeżeli powyższy kod blok jest kopiowany do
.bashrc
pliku, nie tylko funkcje powłoki_guard(), _rspec()
i_rake()
być zadeklarowane podczas logowania, ale_gem_dec()
funkcja będzie dostępna do realizacji w dowolnym momencie w swojej skorupie prompt (lub inaczej) i tak nowy szablonowe funkcje mogą zadeklaruj, kiedy tylko chcesz:I dzięki @Andrew za pokazanie mi, że nie zostaną zjedzeni przez
for loop.
ALE JAK?
Używam
3
powyższego deskryptora pliku, aby trzymaćstdin, stdout, and stderr, or <&0 >&1 >&2
się z dala od przyzwyczajenia - chociaż, podobnie jak w przypadku kilku innych domyślnych środków ostrożności, które tu wdrażam - ponieważ wynikowa funkcja jest tak prosta, że tak naprawdę nie jest konieczna. Jest to jednak dobra praktyka. Dzwonienieshift $#
to kolejna z tych niepotrzebnych środków ostrożności.Mimo to, gdy plik jest określony jako
<input
albo>output
z[optional num]<file
lub[optional num]>file
przekierowanie jądro odczytuje go w deskryptorze pliku, które mogą być dostępne za pośrednictwemcharacter device
specjalnych plików/dev/fd/[0-9]*
. Jeśli[optional num]
specyfikator zostanie pominięty, wówczas0<file
przyjmuje się dla danych wejściowych i1>file
wyjściowych. Rozważ to:A ponieważ a
here-document
jest jedynie sposobem na opisanie pliku wewnątrz bloku kodu, kiedy:Równie dobrze moglibyśmy:
Z jednym bardzo ważnym rozróżnieniem. Jeśli nie tematyce następnie powłoka oceni go do muszli , takich jak:
"'\quote'"
<<"'\LIMITER"'
here-document
$expansion
Tak więc, dla
_gem_dec()
,3<<-FUNC here-document
jest oceniany jako plik na wejściu, tak samo jak byłoby, gdyby to było3<~/some.file
wyjątkiem , że ponieważ mamy opuścićFUNC
ogranicznik darmo cytatów, jest to pierwszy oceniano$expansion.
Ważną rzeczą jest to, że jest to wejście, znaczenie istnieje_gem_dec(),
tylko dlatego, że jest również analizowany przed uruchomieniem_gem_dec()
funkcji, ponieważ nasza powłoka musi ją odczytać i ocenić$expansions
przed przekazaniem jej jako danych wejściowych.Zróbmy
guard,
na przykład:Więc najpierw powłoka musi obsłużyć dane wejściowe, co oznacza odczyt:
W deskryptor pliku 3 i ocenianie go pod kątem rozszerzenia powłoki. Jeśli w tej chwili prowadziłeś:
Lub:
Ponieważ oba są równoważnymi poleceniami, zobaczysz *:
... przedtem w ogóle wykonuje się kod w funkcji. Jest to funkcja użytkownika
<input
, po wszystkim. Aby uzyskać więcej przykładów, zobacz moją odpowiedź na inne pytanie tutaj .(* Technicznie nie jest to do końca prawdą. Ponieważ używam wiodącego
-dash
przedhere-doc limiter
powyższym, powyższe byłoby usprawiedliwione do lewej strony. Ale użyłem-dash
tak, żebym mógł przede wszystkim<tab-insert>
dla czytelności, więc nie zamierzam usuwać<tab-inserts>
wcześniej oferując ci do przeczytania ...)Najmilszą częścią tego jest cytowanie - zauważ, że
'"
cytaty pozostały i tylko\
cytaty zostały usunięte. Prawdopodobnie z tego powodu bardziej niż jakiegokolwiek innego, jeśli będziesz musiał dwukrotnie ocenić powłokę$expansion
, polecę to,here-document
ponieważ cytaty są znacznie łatwiejsze niżeval
.W każdym razie, teraz powyższy kod jest dokładnie tak, jak plik wprowadzony, jak
3<~/heredoc.file
tylko czekanie na uruchomienie_gem_dec()
funkcji i zaakceptowanie jej danych wejściowych/dev/fd/3
.Więc kiedy zaczynamy,
_gem_dec()
najpierw rzucam wszystkie parametry pozycyjne, ponieważ naszym następnym krokiem jest podwójnie ocenione rozszerzenie powłoki i nie chcę, aby którykolwiek z zawartych elementów$expansions
był interpretowany jako jeden z moich bieżących$1 $2 $3...
parametrów. Więc ja:shift
odrzuca tyle,positional parameters
ile określisz i zaczyna od$1
tego, co pozostało. Tak więc, gdybym wywołał_gem_dec one two three
w linii poleceń_gem_dec's $1 $2 $3
parametry pozycyjne byłyby,one two three
a całkowita bieżąca liczba pozycyjna, lub$#
wynosiłaby 3. Gdybym wtedy wywołałshift 2,
wartościone
itwo
zostałbyshift
usunięty , wartość$1
zmieniłaby się nathree
i$#
rozszerzyłaby się do 1. Więcshift $#
po prostu wyrzuca je wszystkie. Robienie tego jest ściśle zapobiegawcze i jest tylko nawykiem, który rozwinąłem po zrobieniu tego rodzaju rzeczy przez jakiś czas. Oto(subshell)
trochę dla jasności:W każdym razie następnym krokiem jest magia. Jeśli pojawi
. ~/some.sh
się monit powłoki, wszystkie funkcje i zmienne środowiskowe zadeklarowane w programie~/some.sh
będą mogły być wywoływane po zachęcie powłoki. Tak samo jest tutaj, z wyjątkiem my plik specjalny dla naszego deskryptor pliku, lub - co jest gdzie nasz plik w linii został pathed - i mamy zadeklarowane naszą funkcję. I tak to działa.. source
character device
. /dev/fd/3
here-document
Teraz robi wszystko, co
_guard
powinna zrobić twoja funkcja.Uzupełnienie:
Świetny sposób na zapisanie pozycji:
EDYTOWAĆ:
Kiedy po raz pierwszy odpowiedziałem na to pytanie, bardziej skupiłem się na problemie zadeklarowania powłoki
function()
zdolnej do zadeklarowania innych funkcji, które pozostaną w bieżącym$ENV
etapie prasowania powłoki, niż na tym, co pytający zrobiłby z tymi stałymi funkcjami. Od tego czasu zdałem sobie sprawę, że moje pierwotnie zaproponowane rozwiązanie, w którym3<<-FUNC
przyjęło formę:Nie będzie prawdopodobnie pracował zgodnie z oczekiwaniami do pytającego, bo specjalnie zmienił nazwę deklaratywnego funkcja jest od
$1
celu_${1}
, który, jeśli nazywa się tak jak_gem_dec guard
na przykład, spowodowałoby_gem_dec
deklarowania funkcji o nazwie_guard
a nie tylkoguard
.Uwaga: Takie zachowanie jest dla mnie kwestią przyzwyczajenia - zazwyczaj działam z założeniem, że funkcje powłoki powinny zajmować tylko własne,
_namespace
aby uniknąć włamania się donamespace
powłokicommands
.Nie jest to jednak powszechny nawyk, o czym świadczy użycie
command
wezwania przez pytającego$1
.Dalsze badanie prowadzi mnie do wniosku, że:
Nie działałoby to wcześniej, ponieważ zmieniłem również
$1
wezwaniecommand
do przeczytania:Co nie spowodowałoby wykonania
ruby
funkcji skompilowanej przez funkcję powłoki jako:Mam nadzieję, że zobaczysz (jak ostatecznie to zrobiłem) , że wydaje się, że pytający
command
w ogóle używa tylko pośrednio,namespace
ponieważcommand
woli wywołać plik wykonywalny w$PATH
funkcji powłoki o tej samej nazwie.Jeśli moja analiza jest poprawna (jak mam nadzieję pytający to potwierdzi), to:
Powinien lepiej spełniać te warunki, z wyjątkiem tego, że wywołanie
guard
w wierszu będzie tylko próbowało wykonać plik wykonywalny w$PATH
nazwie,guard
podczas gdy wywołanie_guard
w wierszu sprawdzi, czyGemfile's
istnieje i odpowiednio się skompiluje lub uruchomiguard
plik wykonywalny w$PATH
. W ten sposóbnamespace
jest chroniony i, przynajmniej tak, jak go postrzegam, zamiary pytającego są nadal spełnione.W rzeczywistości, zakładając, że nasza funkcja powłoki
_${1}()
i plik wykonywalny${PATH}/${1}
to jedyne dwa sposoby, w jakie nasza powłoka może interpretować wywołanie albo,$1
albo_${1}
użyciecommand
funkcji w ogóle jest teraz całkowicie zbędne. Mimo to pozwoliłem temu pozostać, ponieważ nie lubię popełniać tego samego błędu dwa razy ... w każdym razie z rzędu.Jeśli jest to nie do przyjęcia dla pytającego, a on / ona wolałaby
_
całkowicie się pozbyć , to w obecnej formie edycja_underscore
wyjścia powinna być wszystkim, co pytający musi zrobić, aby spełnić jego / jej wymagania, tak jak je rozumiem.Oprócz tej zmiany zredagowałem również funkcję, aby używać
&&
i / lub||
warunek zwarcia powłoki zamiast oryginalnejif/then
składni. W ten sposóbcommand
instrukcja jest oceniana w ogóle tylko wtedy, gdy jejGemfile
nie ma$PATH
. Ta modyfikacja wymaga jednak dodania,return $?
aby upewnić się, żebundle
instrukcja nie zostanie uruchomiona, jeśli zdarzenieGemfile
nie istnieje, aleruby $1
funkcja zwraca wartość inną niż 0.Na koniec należy zauważyć, że to rozwiązanie implementuje tylko przenośne konstrukcje powłoki. Innymi słowy, powinno to dawać identyczne wyniki w dowolnej powłoce, która twierdzi, że jest zgodna z POSIX. Choć oczywiście byłoby dla mnie nonsensem twierdzenie, że każdy system zgodny z POSIX musi obsługiwać
ruby bundle
dyrektywę, przynajmniej imperatywne powłoki wywołujące go powinny zachowywać się tak samo, niezależnie od tego, czy wywołująca powłoka jest,sh
czy teżdash
. Również powyższe prace będą zgodnie z oczekiwaniami (zakładając, przynajmniej w połowie-saneshopts
w każdym razie) w obubash
izsh
.źródło
~/.bashrc
i wywołuję. ~/.bashrc
, a następnie te trzy funkcje są wykonywane. Może zachowanie różni się w zależności od środowiska, więc do pytania dodałem swoje środowisko. Poza tym nie mogłem zrozumieć, dlaczego_guard ; _rspec ; _rake
potrzebna jest ostatnia linia . Szukałemshift
i deskryptor pliku, wygląda na to, że są one poza moim obecnym zrozumieniem.źródło