Zazwyczaj pracuję z serwerów Ubuntu LTS, który z tego co rozumiem dowiązania /bin/sh
do /bin/dash
. Wiele innych dystrybucjach though dowiązaniem /bin/sh
do /bin/bash
.
Z tego rozumiem, że jeśli skrypt używa #!/bin/sh
na górze, może nie działać w ten sam sposób na wszystkich serwerach?
Czy istnieje sugerowana praktyka, w której powłoce należy używać skryptów, gdy chce się maksymalnie przenosić te skrypty między serwerami?
#!/bin/sh
i nie używaj niczego innego niż to, co zapewnia oryginalna powłoka.Odpowiedzi:
Istnieją około cztery poziomy przenośności dla skryptów powłoki (jeśli chodzi o linię shebang):
Najbardziej przenośny: użyj
#!/bin/sh
shebang i używaj tylko podstawowej składni powłoki określonej w standardzie POSIX . Powinno to działać na prawie każdym systemie POSIX / unix / linux. (Cóż, z wyjątkiem Solaris 10 i wcześniejszych, który miał prawdziwą starszą powłokę Bourne'a, wyprzedzając POSIX tak niezgodny, jak/bin/sh
.)Drugi najbardziej przenośny: użyj linii shebang
#!/bin/bash
(lub#!/usr/bin/env bash
) i trzymaj się funkcji bash v3. Działa to na każdym systemie, który ma bash (w oczekiwanej lokalizacji).Trzecia najbardziej przenośna: użyj linii shebang
#!/bin/bash
(lub#!/usr/bin/env bash
) i użyj funkcji bash v4. Nie powiedzie się to w każdym systemie, który ma bash v3 (np. MacOS, który musi go używać ze względów licencyjnych).Najmniej przenośny: użyj
#!/bin/sh
shebang i użyj rozszerzeń bash do składni powłoki POSIX. Nie powiedzie się to w każdym systemie, który ma coś innego niż bash dla / bin / sh (np. Najnowsze wersje Ubuntu). Nigdy tego nie rób; to nie tylko problem ze zgodnością, to po prostu źle. Niestety wielu ludzi popełnia błąd.Moja rekomendacja: użyj najbardziej konserwatywnej z pierwszych trzech, która zapewnia wszystkie funkcje powłoki potrzebne do skryptu. Aby uzyskać maksymalną przenośność, użyj opcji nr 1, ale z mojego doświadczenia wynika, że niektóre funkcje bash (takie jak tablice) są na tyle pomocne, że pójdę z numerem 2.
Najgorsze, co możesz zrobić, to # 4, używając niewłaściwego shebang. Jeśli nie masz pewności, jakie funkcje są podstawowe POSIX, a które są rozszerzeniami bash, albo trzymaj się bash shebang (tj. Opcja # 2), lub dokładnie przetestuj skrypt za pomocą bardzo podstawowej powłoki (np. Dash na serwerach Ubuntu LTS). Wiki Ubuntu ma dobrą listę baszizmów, na które należy uważać .
Jest kilka bardzo dobrych informacji o historii i różnicach między powłokami w pytaniu na Unixa i Linuksa „Co to znaczy być kompatybilnym z sh?” oraz pytanie Stackoverflow „Różnica między sh i bash” .
Pamiętaj też, że powłoka nie jest jedyną rzeczą, która różni się w zależności od systemu; jeśli jesteś przyzwyczajony do Linuksa, przywykłeś do poleceń GNU, które mają wiele niestandardowych rozszerzeń, których możesz nie znaleźć w innych systemach uniksowych (np. bsd, macOS). Niestety nie ma tutaj prostej reguły, wystarczy znać zakres zmienności używanych poleceń.
Jednym z najniebezpieczniejszych poleceń w zakresie przenośności jest jednym z najbardziej podstawowych:
echo
. Za każdym razem, gdy użyjesz go z dowolnymi opcjami (np.echo -n
Lubecho -e
), lub z dowolnymi znakami ucieczki (odwrotnymi ukośnikami) w ciągu do wydrukowania, różne wersje zrobią różne rzeczy. Za każdym razem, gdy chcesz wydrukować ciąg bez podawania wiersza po nim lub ze znakami ucieczki w ciągu, użyjprintf
zamiast tego (i dowiedz się, jak to działa - jest to bardziej skomplikowane niżecho
jest).ps
Polecenia jest także bałagan .Inną ogólną rzeczą, na którą należy zwrócić uwagę, są najnowsze rozszerzenia / GNUish do składni opcji poleceń: stary (standardowy) format polecenia polega na tym, że po poleceniu następują opcje (z pojedynczym myślnikiem, a każda opcja to jedna litera), po którym następuje argumenty poleceń. Ostatnie (i często nieprzenośne) warianty obejmują długie opcje (zwykle wprowadzane wraz z
--
), pozwalające na pojawienie się opcji po argumentach i używanie ich--
do oddzielania opcji od argumentów.źródło
sh
), więc/bin/sh
nie gwarantuje się, że jest to właściwa ścieżka. Najbardziej przenośnym jest wtedy, aby w ogóle nie określać żadnego odcinka ani dostosowywać odcinka do używanego systemu operacyjnego.#!/bin/sh
się#!/bin/bash
jednak skrypt działał idealnie. (Na moją obronę ten scenariusz ewoluował z czasem z jednego, który naprawdę potrzebował tylko sh-ismów, do tego, który polegał na zachowaniu przypominającym bash).ksh
zachowania, a nie Bourne. Większość dystrybucji Linuksa po FHS to/bin/sh
symboliczne łącze do powłoki, którą wybierają, aby zapewnić zgodność z POSIX, zwyklebash
lubdash
.W
./configure
skrypcie przygotowującym język TXR do budowania napisałem następujący prolog dla lepszej przenośności. Skrypt sam się uruchomi, nawet jeśli#!/bin/sh
jest niezgodny ze standardem POSIX starej powłoki Bourne'a. (Każde wydanie buduję na maszynie wirtualnej Solaris 10).Chodzi o to, że znajdujemy lepszą powłokę niż ta, w której działamy, i ponownie uruchamiamy skrypt za pomocą tej powłoki.
txr_shell
Zmienna jest ustawiona tak, że skrypt ponownie wykonywane wie, że jest ponownie wykonywane rekurencyjne instancji.(W moim skrypcie
txr_shell
zmienna jest następnie używana do dokładnie dwóch celów: po pierwsze jest drukowana jako część wiadomości informacyjnej na wyjściu skryptu. Po drugie, jest instalowana jakoSHELL
zmienna wMakefile
, więcmake
użyje tego shell również do wykonywania przepisów).W systemie, w którym
/bin/sh
jest myślnik, widać, że powyższa logika znajdzie/bin/bash
i ponownie uruchomi skrypt za jego pomocą.W przypadku systemu Solaris 10 uruchomi się
/usr/xpg4/bin/sh
, jeśli nie zostanie znalezione Bash.Prolog napisany jest w konserwatywnym dialekcie powłoki, używanym
test
do testów istnienia plików, a${@+"$@"}
sztuczka polegająca na rozszerzaniu argumentów uwzględniających niektóre zepsute stare powłoki (co byłoby po prostu"$@"
, gdybyśmy byli w powłoce zgodnej z POSIX).źródło
x
Nikt nie potrzebowałby hakowania, gdyby zastosowano prawidłowe cytowanie, ponieważ sytuacje, które nakazały temu idiomowi otaczać obecnie nieaktualne wywołania testowe-a
lub-o
łączenie wielu testów.test x$whatever
, co tam robię, wygląda jak cebula w lakierze. Jeśli nie możemy ufać zepsutej starej powłoce, aby cytować, to ostatnia${@+"$@"}
próba jest bezcelowa.Wszystkie odmiany języka powłoki Bourne'a są obiektywnie okropne w porównaniu do współczesnych języków skryptowych, takich jak Perl, Python, Ruby, node.js, a nawet (prawdopodobnie) Tcl. Jeśli musisz zrobić coś nawet trochę skomplikowanego, na dłuższą metę będziesz szczęśliwszy, jeśli użyjesz jednego z powyższych zamiast skryptu powłoki.
Jedyną zaletą, jaką język powłoki ma nadal w porównaniu z nowszymi językami, jest to, że istnieje pewność, że coś , co nazywa się samym,
/bin/sh
istnieje na wszystkim, co może być uniksowe. Jednak to coś może nawet nie być zgodne z POSIX; wiele starszych, unikatowych uniksów zamroziło język/bin/sh
i narzędzia w domyślnej PATH przed zmianami wymaganymi przez Unix95 (tak, Unix95, dwadzieścia lat temu i wciąż rośnie). Może być zestaw Unix95, a nawet POSIX.1-2001, jeśli masz szczęście, narzędzia w katalogu innym niż domyślna ŚCIEŻKA (np./usr/xpg4/bin
), Ale nie gwarantuje się, że istnieją.Jednak podstawy Perla częściej występują w arbitralnie wybranej instalacji Uniksa niż Bash. (Przez „podstawy Perla” mam na myśli, że
/usr/bin/perl
istnieje i jest to pewna , być może dość stara, wersja Perla 5, a jeśli masz szczęście, dostępny jest także zestaw modułów dostarczonych z tą wersją interpretera).W związku z tym:
Jeśli piszesz coś, co musi działać wszędzie, co rzekomo jest uniksowe (np. Skrypt „konfiguruj”), musisz go użyć
#! /bin/sh
i nie musisz używać żadnych rozszerzeń. W dzisiejszych czasach napisałbym w tej sytuacji powłokę zgodną z POSIX.1-2001, ale byłbym przygotowany na załatanie POSIXizmu, gdyby ktoś poprosił o wsparcie dla zardzewiałego żelaza.Ale jeśli nie piszesz czegoś, co musi działać wszędzie, to w momencie, gdy masz ochotę w ogóle użyć bashizmu, powinieneś przerwać i przepisać całość w lepszym języku skryptowym. Twoje przyszłe ja będzie ci wdzięczne.
(Tak, gdy jest to stosowne korzystać z rozszerzeń bash pierwszej kolejności: nigdy do drugiej kolejności: tylko przedłużyć interaktywne środowisko Bash - np dostarczenie inteligentnych zakładki dopełniania i fantazyjne wyświetlanymi?.).
źródło
find ... -print0 |while IFS="" read -rd "" file; do ...; done |tar c --null -T -
. Nie byłoby możliwe, aby działał poprawnie bez bashisms (read -d ""
) i rozszerzeń GNU (find -print0
,tar --null
). Ponowne wdrożenie tej linii w Perlu byłoby znacznie dłuższe i bardziej niezgrabne. Jest to sytuacja, w której używanie bashism i innych rozszerzeń innych niż POSIX jest właściwą rzeczą.perl -MFile::Find -e 'our @tarcmd = ("tar", "c"); find(sub { return unless ...; ...; push @tarcmd, $File::Find::name }, "."); exec @tarcmd
trochę jak Zauważ, że nie tylko nie potrzebujesz bashizmów, ale nie potrzebujesz żadnych rozszerzeń tar GNU. (Jeśli chodzi o długość wiersza poleceń lub jeśli chcesz, aby skanowanie i tar działały równolegle, potrzebujesztar --null -T
).find
w tym, że w moim scenariuszu zwrócono ponad 50 000 nazw plików, więc twój skrypt prawdopodobnie przekroczy limit długości argumentów. Prawidłowa implementacja, która nie używa rozszerzeń innych niż POSIX, musiałaby budować dane wyjściowe tar stopniowo lub być może użyć Archive :: Tar :: Stream w kodzie Perla. Oczywiście można to zrobić, ale w tym przypadku skrypt Bash jest o wiele szybszy do napisania i ma mniej płyt kotłowych, a tym samym mniej miejsca na błędy.find ... -print0 |perl -0ne '... print ...' |tar c --null -T -
. Ale pytania są następujące: 1) Używamfind -print0
itar --null
tak, po co więc unikać bashizmuread -d ""
, co jest dokładnie tym samym rodzajem (rozszerzenie GNU do obsługi separatora zerowego, które w przeciwieństwie do Perla może kiedyś stać się POSIX )? 2) Czy Perl jest naprawdę bardziej rozpowszechniony niż Bash? Ostatnio korzystam z obrazów Dockera i wiele z nich ma Bash, ale nie ma Perla. Nowe skrypty są tam częściej uruchamiane niż na starożytnych Unices.