Mam skrypt, który chcę uruchomić na dwóch komputerach. Te dwie maszyny pobierają kopie skryptu z tego samego repozytorium git. Skrypt musi działać z odpowiednim tłumaczem (np zsh
.).
Niestety, zarówno env
i zsh
mieszkają w różnych lokalizacjach na komputerach lokalnych i zdalnych:
Zdalna maszyna
$ which env
/bin/env
$ which zsh
/some/long/path/to/the/right/zsh
Maszyna lokalna
$ which env
/usr/bin/env
$which zsh
/usr/local/bin/zsh
Jak skonfigurować shebang, aby uruchomienie skryptu jak /path/to/script.sh
zawsze używa Zsh
dostępnych w PATH
?
env
nie ma go w katalogu / bin i / usr / bin? Spróbujwhich -a env
potwierdzić.Odpowiedzi:
Nie można rozwiązać tego bezpośrednio poprzez shebang, ponieważ shebang jest czysto statyczny. To, co możesz zrobić, to mieć trochę »najmniej wspólnego mnożnika« (z perspektywy powłoki) w shebang i ponownie uruchomić skrypt z właściwą powłoką, jeśli ten LCM nie jest zsh. Innymi słowy: czy skrypt wykonywany przez shell znaleźć na wszystkich systemach testowych dla
zsh
-tylko funkcji i jeśli zakrętach przetestować fałszywe, mieć skryptexec
zzsh
, gdzie test będzie sukces i po prostu kontynuować.Jedną wyjątkową cechą jest
zsh
na przykład obecność$ZSH_VERSION
zmiennej:W tym prostym przypadku skrypt jest najpierw wykonywany przez
/bin/sh
(wszystkie systemy uniksopodobne po latach 80. rozumieją#!
i mają/bin/sh
albo Bourne'a, albo POSIXA, ale nasza składnia jest kompatybilna z obydwoma). Jeśli nie$ZSH_VERSION
jest ustawiony, sam skrypt przechodzi przez . Jeśli jest ustawiony (odpowiednio, skrypt jest już uruchamiany ), test jest po prostu pomijany. Voilà.exec
zsh
$ZSH_VERSION
zsh
To się nie udaje, jeśli
zsh
w$PATH
ogóle nie ma.Edycja: Aby upewnić się, to tylko w zwykłych miejscach, można użyć coś podobnego
exec
zsh
Może to uchronić Cię przed przypadkowym
exec
zaistnieniem czegoś w twoim ciele, czego$PATH
niezsh
oczekujesz.źródło
zsh
z nich$PATH
nie jest tym, którego się spodziewasz.zsh
binarny w standardowych lokalizacjach jest naprawdęzsh
.zsh
sobie pytanie , gdzie to jestzsh -c 'whence zsh'
. Mówiąc prościej, możesz po prostucommand -v zsh
. Zobacz moją odpowiedź, jak dynamicznie śledzić#!bang
.zsh
binarnego z w$PATH
celu uzyskania ścieżkizsh
pliku binarnego nie rozwiązałoby problemu wskazanego przez @RyanReich, prawda? :)zsh
sam się zabijesz , nie, chyba nie. Ale jeśli osadzisz wynikowy ciąg w haszu mieszania, a następnie wykonasz własny skrypt, przynajmniej wiesz, co dostajesz. Nadal byłby to prostszy test niż zapętlenie.Przez lata korzystałem z czegoś podobnego do radzenia sobie z różnymi lokalizacjami Bash w systemach, które wymagały uruchomienia moich skryptów.
Bash / Zsh / itp.
Powyższe można łatwo dostosować dla różnych tłumaczy. Kluczowe jest to, że ten skrypt początkowo działa jako powłoka Bourne'a. Następnie rekurencyjnie nazywa się po raz drugi, ale analizuje wszystko powyżej komentarza
### BEGIN
za pomocąsed
.Perl
Oto podobna sztuczka dla Perla:
Ta metoda wykorzystuje zdolność Perla, gdy dany plik do uruchomienia przeanalizuje ten plik, pomijając wszystkie wiersze znajdujące się przed nim
#! perl
.źródło
$*
zamiast"$@"
, bezużyteczne użycie eval, status wyjścia niezgłoszony (nie używałeśexec
dla pierwszego), brak-
/--
, komunikaty o błędach nie na stderr, 0 status wyjścia dla warunków błędu , używając / bin / sh dla LIN_BASH, bezużyteczny średnik (kosmetyczny), używając wszystkich wielkich liter dla zmiennych innych niż env.uname -s
jest jakuname
(uname jest dla nazwy Uniksa). Zapomniałeś wspomnieć, że pomijanie jest uruchamiane przez-x
opcję doperl
.UWAGA: @ jw013 zgłasza następujący nieobsługiwany sprzeciw w komentarzach poniżej:
Odpowiedziałem mu zarzutów zabezpieczeń, wskazując, że jakieś specjalne uprawnienia są wymagane tylko raz na instalacji / aktualizacji działań w celu instalacji / aktualizacji na samodzielne instalowanie skryptu - co ja osobiście nazywam całkiem bezpieczne. Wskazałem mu także
man sh
odniesienie do osiągania podobnych celów za pomocą podobnych środków. W tym czasie nie zadałem sobie trudu, aby wskazać, że niezależnie od wad bezpieczeństwa lub ogólnie nieadekwatnych praktyk, które mogą, ale nie muszą być reprezentowane w mojej odpowiedzi, były one bardziej zakorzenione w samym pytaniu niż w mojej odpowiedzi na to:Niezadowolony @ jw013 nadal sprzeciwił się, przekazując swój dotychczas nieobsługiwany argument co najmniej kilkoma błędnymi stwierdzeniami:
Na pierwszym miejscu:
JEDYNY WYKONALNY KOD W KAŻDYM WYKONALNYM SKRIPIE ŁUSKI JEST TO
#!
SAM(chociaż nawet oficjalnie nie
#!
jest określony )Skrypt powłoki jest tylko plik tekstowy - aby mogła ona mieć żadnego wpływu na wszystko należy czytać przez innego pliku wykonywalnego, jego instrukcje następnie interpretowane przez tego innego pliku wykonywalnego, zanim wreszcie inny plik wykonywalny następnie wykonuje swoją interpretację z następujących Skrypt powłoki. Jest to możliwe za wykonanie skryptu powłoki, aby zaangażować mniej niż dwa pliki. Istnieje możliwy wyjątek we
zsh
własnym kompilatorze, ale dzięki temu mam niewielkie doświadczenie i nie ma tu żadnej reprezentacji.Hashang skryptu powłoki musi wskazywać na zamierzonego interpretera lub zostać odrzucony jako nieistotny.
Skorupa'S TOKEN UZNANIE / WYKONANIE zachowanie jest zdefiniowane NORMY
Powłoka ma dwa podstawowe tryby parsowania i interpretacji danych wejściowych: albo bieżące dane wejściowe definiują a,
<<here_document
albo definiują{ ( command |&&|| list ) ; } &
- innymi słowy, powłoka interpretuje token jako ogranicznik polecenia, które powinno wykonać po przeczytaniu w lub jako instrukcje, aby utworzyć plik i zamapować go na deskryptorze pliku dla innego polecenia. Otóż to.Podczas interpretacji poleceń w celu wykonania powłoki rozgraniczają tokeny na zestawie słów zastrzeżonych. Gdy powłoka napotyka otwór żeton musi czytać dalej na liście poleceń aż lista jest albo ograniczona przez zamknięciem tokena takie jak nowej linii - w stosownych przypadkach - czy zamknięcie tokena jak
})
na({
przed egzekucją.Powłoka rozróżnia proste i złożone polecenie. Polecenie złożone jest zbiorem poleceń, które należy odczytać przed wykonaniem, ale powłoka nie wykonuje
$expansion
żadnej z prostych składowych poleceń, dopóki nie wykona pojedynczo każdego z nich.Tak więc w poniższym przykładzie
;semicolon
słowa zastrzeżone ograniczają poszczególne proste polecenia, podczas gdy\newline
znak, który nie jest klawiszem ucieczki, rozgranicza między dwoma poleceniami złożonymi:Jest to uproszczenie wytycznych. Staje się znacznie bardziej skomplikowane, gdy weźmie się pod uwagę wbudowane powłoki, podpowłoki, bieżące środowisko itp., Ale dla moich celów tutaj wystarczy.
Mówiąc o wbudowanych i listach poleceń, a
function() { declaration ; }
jest jedynie sposobem na przypisanie polecenia złożonego do prostego polecenia. Powłoka nie może wykonywać żadnych$expansions
instrukcji w samej deklaracji - aby ją uwzględnić<<redirections>
- ale musi przechowywać definicję jako pojedynczy, dosłowny ciąg znaków i wykonywać ją jako specjalną powłokę wbudowaną po wywołaniu.Tak więc funkcja powłoki zadeklarowana w wykonywalnym skrypcie powłoki jest przechowywana w pamięci powłoki tłumaczącej w postaci dosłownej - nie jest rozszerzana o dołączone tutaj dokumenty jako dane wejściowe - i wykonywana niezależnie od pliku źródłowego za każdym razem, gdy jest wywoływana jako wbudowana powłoka - tak długo, jak trwa obecne środowisko powłoki.
<<HERE-DOCUMENT
JEST inline FILEZobaczysz? Dla każdej powłoki powyżej powłoki tworzy plik i mapuje go na deskryptor pliku. W
zsh, (ba)sh
powłoce tworzy się zwykły plik/tmp
, zrzuca dane wyjściowe, mapuje je na deskryptor, a następnie usuwa/tmp
plik, aby kopia deskryptora jądra pozostała.dash
unika tych bzdur i po prostu upuszcza przetwarzanie danych wyjściowych do anonimowego|pipe
pliku skierowanego na<<
cel przekierowania .To sprawia
dash
, że:funkcjonalnie równoważny do
bash
:podczas gdy
dash
implementacja jest przynajmniej przenośna POSIXly.KTÓRE TWORZĄ KILKA PLIKÓW
Więc w odpowiedzi poniżej, kiedy to robię:
Zdarza się:
Po raz pierwszy
cat
zawartość co plik powłoka utworzona naFILE
do./file
sprawiają, że plik wykonywalny, a następnie uruchom go.Jądro interpretuje
#!
wywołania i/usr/bin/sh
z<read
deskryptorem pliku przypisanym do./file
.sh
odwzorowuje ciąg do pamięci składający się z polecenia złożonego rozpoczynającego się_fn()
i kończącego się naSCRIPT
.Kiedy
_fn
nazywa,sh
musi najpierw interpretują następnie mapę do deskryptora pliku zdefiniowanego w<<SCRIPT...SCRIPT
przed powołując_fn
jako specjalny wbudowane narzędzie, ponieważSCRIPT
jest_fn
„s<input.
Wyjście przez ciągi
printf
icommand
są wypisywane do_fn
„s Standard-out>&1
- który jest przekierowany do prądu powłoki użytkownikaARGV0
- albo$0
.cat
łączy swój deskryptor pliku ze<&0
standardowym wejściem -SCRIPT
->
zARGV0
argumentem obciętej bieżącej powłoki , lub$0
.Wypełnianie już wczytanej bieżącej komendy złożonej ,
sh exec
jest wykonywalnym - i nowo przepisanym -$0
argumentem.Od czasu
./file
wywołania, aż zawarte w nim instrukcje określają, że powinien on byćexec
ponownie,sh
odczytuje je pojedynczym złożonym poleceniem naraz, gdy je wykonuje, podczas gdy./file
sam nic nie robi, poza radością przyjmując jego nową zawartość. Pliki, które faktycznie działają, są/usr/bin/sh, /usr/bin/cat, /tmp/sh-something-or-another.
DZIĘKI PO WSZYSTKIM
Więc kiedy @ jw013 określa, że:
... wśród swojej błędnej krytyki tej odpowiedzi tak naprawdę nieświadomie akceptuje jedyną zastosowaną tutaj metodę, która w zasadzie polega na:
ODPOWIEDŹ
Wszystkie odpowiedzi tutaj są dobre, ale żadna z nich nie jest w pełni poprawna. Wydaje się, że wszyscy twierdzą, że nie można dynamicznie i trwale podążać ścieżką
#!bang
. Oto demonstracja utworzenia niezależnego shebang od ścieżki:PRÓBNY
WYNIK
Zobaczysz? Sprawiamy, że skrypt sam się nadpisuje. I zdarza się to tylko raz po
git
synchronizacji. Od tego momentu ma właściwą ścieżkę w linii #! Bang.Teraz prawie wszystko to po prostu puch. Aby to zrobić bezpiecznie, potrzebujesz:
Funkcja zdefiniowana u góry i wywołana u dołu, która wykonuje zapis. W ten sposób przechowujemy wszystko, czego potrzebujemy w pamięci i upewniamy się, że cały plik został wczytany, zanim zaczniemy nad nim zapisywać.
Jakiś sposób na określenie ścieżki.
command -v
jest całkiem dobry do tego.Heredocs naprawdę pomagają, ponieważ są to rzeczywiste pliki. W międzyczasie będą przechowywać Twój skrypt. Możesz także używać ciągów, ale ...
Musisz upewnić się, że powłoka odczytuje polecenie, które zastępuje skrypt na tej samej liście poleceń, co ta, która go wykonuje.
Popatrz:
Zauważ, że przesunąłem
exec
polecenie tylko o jedną linię. Teraz:Nie dostaję drugiej połowy wyniku, ponieważ skrypt nie może odczytać następnego polecenia. Mimo to, ponieważ jedynym brakującym poleceniem było ostatnie:
Skrypt przyszedł tak, jak powinien - głównie dlatego, że wszystko było w heredoc - ale jeśli nie zaplanujesz go poprawnie, możesz obciąć strumień plików, co przydarzyło mi się powyżej.
źródło
man command
sprzeczną opinię.man command
sprzeczną opinię - nie znajdując takiej. Czy możesz skierować mnie do konkretnej sekcji / akapitu, o której mówiłeś?man sh
- wyszukaj „command -v”. Wiedziałem, że to na jednej zeman
stron, na które patrzyłem innego dnia.command -v
przykład, o którym mówiłeśman sh
. Jest to normalnie wyglądający skrypt instalatora, a nie samodomodujący . Nawet samodzielne instalatory zawierają tylko dane wejściowe przed modyfikacją i wysyłają swoje modyfikacje gdzie indziej. Nie przepisują się tak, jak polecasz.Oto jeden ze sposobów na posiadanie skryptu samomodyfikującego, który naprawia jego shebang. Ten kod należy dołączyć do rzeczywistego skryptu.
Niektóre komentarze:
Powinien zostać uruchomiony raz przez osobę, która ma uprawnienia do tworzenia i usuwania plików w katalogu, w którym znajduje się skrypt.
Używa tylko starszej składni powłoki bourne, ponieważ pomimo powszechnego przekonania
/bin/sh
nie jest gwarantowana, że jest powłoką POSIX, nawet w systemach operacyjnych zgodnych z POSIX.Ustawiono PATH na zgodny z POSIX, a następnie listę możliwych lokalizacji zsh, aby uniknąć wybrania „fałszywego” zsh.
Jeśli z jakiegoś powodu skrypt samomodyfikujący nie jest mile widziany, trywialne byłoby rozpowszechnianie dwóch skryptów zamiast jednego, pierwszy to ten, który chcesz załatać, a drugi, ten, który zasugerowałem nieco zmodyfikowany, aby przetworzyć pierwszy.
źródło
/bin/sh
Punkt jest jeden dobry - ale w tym przypadku potrzebny jest premodified#!
w ogóle? I czy nie jestawk
tak prawdopodobne, że będzie tak fałszywy, jakzsh
jest?sed -i
zresztą robi wszystko GNU . Osobiście uważam, że$PATH
problem zanotowany w komentarzach do innej odpowiedzi, który rozwiązujesz tak bezpiecznie, jak mogę to tutaj przedstawić w kilku wierszach, lepiej jest rozwiązać poprzez proste i jednoznaczne zdefiniowanie zależności i / lub rygorystyczne i jawne testowanie - na przykład terazgetconf
może być fałszywe, ale szanse są bliskie zeru, tak samo jak dlazsh
iawk.
$(getconf PATH)
nie jest Bourne.cp $0 $0.old
jest składnią zsh. Odpowiednik Bourne'a byłbycp "$0" "$0.old"
jednakcp -- "$0" "$0.old"