#! / bin / sh vs #! / bin / bash dla maksymalnej przenośności

cherouvim 07/30/2017. 3 answers, 2.886 views
linux bash shell sh

Zwykle pracuję z serwerami Ubuntu LTS, od czego rozumiem dowiązanie symboliczne /bin/sh do /bin/dash . Wiele innych dystrybucji, jednak od symlink /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, na której powłoce można użyć skryptów, gdy chce się maksymalnie przenosić te skrypty między serwerami?

1 Comments
Thorbjørn Ravn Andersen 08/01/2017
Istnieją niewielkie różnice między poszczególnymi powłokami. Jeśli przenośność jest dla Ciebie najważniejsza, użyj #!/bin/sh i nie używaj niczego innego niż oryginalna powłoka.

3 Answers


Gordon Davisson 08/01/2017.

Istnieją cztery poziomy przenośności skryptów powłoki (w odniesieniu do linii shebang):

  1. Większość przenośnych: użyj #!/bin/sh shebang i używaj only podstawowej składni powłoki określonej w standardzie POSIX . Powinno to działać praktycznie na każdym systemie POSIX / unix / linux. (No cóż, poza Solaris 10 i wcześniejszymi wersjami, które miały prawdziwą starszą powłokę Bourne'a, poprzednia wersja POSIX była tak niezgodna, jak /bin/sh .)

  2. Drugi najbardziej przenośny: użyj linii #!/bin/bash (lub #!/usr/bin/env bash ) shebang i trzymaj się funkcji bash v3. To zadziała w każdym systemie, który ma bash (w oczekiwanej lokalizacji).

  3. Trzeci najbardziej przenośny: użyj linii #!/bin/bash (lub #!/usr/bin/env bash ) shebang i użyj funkcji bash v4. To się nie powiedzie w żadnym systemie, który ma bash v3 (np. MacOS, który musi go używać z powodów licencyjnych).

  4. Najmniej przenośny: użyj #!/bin/sh shebang i użyj rozszerzeń bash do składni powłoki POSIX. To się nie powiedzie w żadnym systemie, który ma coś innego niż bash dla / bin / sh (np. Najnowsze wersje Ubuntu). Nigdy tego nie rób; nie jest to tylko problem ze zgodnością, jest po prostu nie w porządku. Niestety, jest to błąd, który popełnia wielu ludzi.

Moje zalecenie: użyj najbardziej konserwatywnego z pierwszych trzech, który zapewnia wszystkie funkcje powłoki potrzebne do skryptu. Aby uzyskać maksymalną przenośność, użyj opcji # 1, ale z mojego doświadczenia wynika, że ​​niektóre funkcje basha (takie jak tablice) są na tyle pomocne, że pójdę z # 2.

Najgorsze, co możesz zrobić, to # 4, używając niewłaściwego shebanga. Jeśli nie masz pewności, które funkcje są podstawowymi POSIX-ami i które są rozszerzeniami bash, bądź trzymaj się bash-shebang (tj. Opcja # 2), lub przetestuj skrypt dokładnie za pomocą bardzo podstawowej powłoki (np. Myślnik na swoich serwerach Ubuntu LTS). Wiki Ubuntu ma dobrą listę bashisms, na które trzeba uważać .

Istnieje bardzo dobra informacja na temat historii i różnic między powłokami w pytaniu Unix i Linux "Co to znaczy być kompatybilnym?" i pytanie Stackoverflow "Różnica między sh i bash" .

Pamiętaj też, że powłoka nie jest jedyną różnicą między różnymi systemami; jeśli jesteś przyzwyczajony do Linuksa, jesteś przyzwyczajony 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 tu prostej reguły, wystarczy znać zakres odmian używanych poleceń.

Jedno z najgorszych poleceń pod względem przenośności jest jednym z najbardziej podstawowych: echo . Za każdym razem, gdy używasz go z dowolnymi opcjami (np. echo -n lub echo -e ) lub z dowolnymi znakami ucieczki (odwrotnymi ukośnikami) w ciągu do drukowania, różne wersje będą robić różne rzeczy. Za każdym razem, gdy chcesz wydrukować ciąg bez wiersza po nim, lub z ucieczkami w łańcuchu, użyj zamiast tego printf (i dowiedz się, jak to działa - jest bardziej skomplikowany niż echo ). Polecenie ps też jest bałaganem .

Kolejną istotną rzeczą do obejrzenia są najnowsze / GNUish rozszerzenia do składni opcji poleceń: stary (standardowy) format polecenia jest taki, że po komendzie występują opcje (z pojedynczym myślnikiem, a każda opcja jest pojedynczą literą), a następnie argumenty polecenia. Ostatnie (i często nieprzenośne) warianty zawierają długie opcje (zwykle wprowadzane za pomocą -- ), pozwalające wybierać opcje po argumentach i używanie -- do oddzielania opcji od argumentów.

5 comments
12 pabouk 07/30/2017
Czwarta opcja to po prostu zły pomysł. Proszę go nie używać.
3 Gordon Davisson 07/30/2017
@ pabouk Całkowicie się zgadzam, więc zredagowałem swoją odpowiedź, aby było to jaśniejsze.
1 jlliagre 07/30/2017
Twoje pierwsze zdanie jest nieco mylące. Standard POSIX nie precyzuje niczego na zewnątrz, mówiąc, że używanie go prowadzi do nieokreślonego zachowania. Co więcej, POSIX nie określa, gdzie musza znajdować się posix, tylko jego nazwa ( sh ), więc /bin/sh nie jest gwarantowaną ścieżką. Najbardziej przenośnym nie jest wtedy określenie żadnego shebangu ani dostosowanie go do używanego systemu operacyjnego.
2 Michael Kjörling 07/30/2017
Natknąłem się na # 4 z moim scenariuszem bardzo niedawno i po prostu nie mogłem zrozumieć, dlaczego to nie działa; w końcu te same polecenia działały solidnie i zrobiły dokładnie to, co chciałem, kiedy próbowałem je bezpośrednio w powłoce. Jak tylko zmieniłem #!/bin/sh na #!/bin/bash , skrypt działał idealnie. (Do mojej obrony, ten skrypt ewoluował w czasie od tego, który tak naprawdę potrzebował tylko sh-isms, do tego, który polegał na działaniu przypominającym bash).
2 jlliagre 07/30/2017
@ MichaelKjörling (Prawdziwa) Powłoka Bourne'a prawie nigdy nie jest dołączana do dystrybucji Linuksa i nie jest zgodna z POSIX. Standardowa powłoka POSIX została utworzona z zachowania ksh , a nie Bourne'a. Większość dystrybucji Linuksa po FHS ma opcję /bin/sh będącą dowiązaniem symbolicznym do powłoki, którą wybiorą, aby zapewnić zgodność z POSIX, zwykle bash lub dash .

Kaz 07/31/2017.

W skrypcie ./configure który przygotowuje język TXR do budowania, napisałem następujący prolog dla lepszej przenośności. Skrypt sam się rozpakuje, nawet jeśli #!/bin/sh jest niezgodną z POSIX-em starym powłoką Bourne'a. (Buduję każde wydanie na maszynie wirtualnej Solaris 10).

#!/bin/sh

# use your own variable name instead of txr_shell;
# adjust to taste: search for your favorite shells

if test x$txr_shell = x ; then
  for shell in /bin/bash /usr/bin/bash /usr/xpg4/bin/sh ; do
    if test -x $shell ; then
       txr_shell=$shell
       break
    fi
  done
  if test x$txr_shell = x ; then
    echo "No known POSIX shell found: falling back on /bin/sh, which may not work"
    txr_shell=/bin/sh
  fi
  export txr_shell
  exec $txr_shell $0 ${@+"$@"}
fi

# rest of the script here, executing in upgraded shell 

Pomysł polega na tym, że znajdujemy lepszą powłokę niż ta, na której uruchamiamy, i ponownie uruchamiamy skrypt za pomocą tej powłoki. txr_shell środowiskowa txr_shell jest ustawiona, aby ponownie wykonany skrypt wiedział, że jest to ponownie wykonana instancja rekursywna.

(W moim skrypcie zmienna txr_shell jest również później używana, w dokładnie dwóch celach: po pierwsze jest drukowana jako część komunikatu informacyjnego na wyjściu skryptu, po drugie jest instalowana jako zmienna SHELL w Makefile , dzięki czemu make użyje tej powłoki również do wykonywania receptur.)

W systemie, gdzie /bin/sh jest myślnikiem, możesz zobaczyć, że powyższa logika znajdzie /bin/bash i ponownie uruchomi skrypt.

Na pudełku Solaris 10, /usr/xpg4/bin/sh włączy się, jeśli nie zostanie znaleziony Bash.

Prolog jest napisany w konserwatywnym dialekcie powłoki, przy użyciu test istnienie pliku oraz sztuczki ${@+"$@"} do rozszerzania argumentów dotyczących niektórych zepsutych starych powłok (które byłyby po prostu "$@" , gdybyśmy byli w powłoce zgodnej z POSIX).

2 comments
Charles Duffy 07/31/2017
Nie trzeba by było hakera x gdyby stosowano odpowiednie cytowanie, ponieważ sytuacje, które nakazują, że idiom otaczają teraz-wycofał wywołania testowe z -a lub -o łącząc wiele testów.
Kaz 07/31/2017
@CharlesDuffy Indeed; test x$whatever tam popełniam, wygląda jak cebula w lakierze. Jeśli nie możemy ufać starej, zepsutej powłoce, aby wykonać cytowanie, ostateczna próba ${@+"$@"} jest bezcelowa.

zwol 07/31/2017.

Wszystkie wariacje 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ć cokolwiek, 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ą przewagą, jaką ma język powłoki w porównaniu z nowszymi językami, jest to, że something , something nazywa się /bin/sh może istnieć na czymkolwiek, co może być Unixem. Jednak coś takiego może nie być zgodne z POSIX; wiele starszych, unikatowych Uniksów zamroziło język zaimplementowany przez /bin/sh i narzędzia w domyślnej PATH prior zmianami wymaganymi przez Unix95 (tak, Unix95, dwadzieścia lat temu i liczenie). Może być zestaw Unix95, a nawet POSIX.1-2001, jeśli masz szczęście, narzędzia w katalogu not w domyślnej PATH (np. /usr/xpg4/bin ), ale nie gwarantują, że istnieją.

Jednak podstawy Perla są more likely że będą obecne na arbitralnie wybranej instalacji Uniksa niż Bash. (Przez "podstawy Perla" mam na myśli /usr/bin/perl i jest some , prawdopodobnie całkiem stara, wersja Perla 5, i jeśli masz szczęście, zestaw modułów dostarczanych z tą wersją interpretera jest również dostępny.)

W związku z tym:

Jeśli piszesz coś, co działa everywhere co może być uniksowe (takie jak skrypt "configure"), musisz użyć #! /bin/sh #! /bin/sh , i nie musisz używać żadnych rozszerzeń. Dziś w tej sytuacji napisałbym powłokę zgodną z POSIX.1-2001, ale byłbym gotowy załatać POSIXisms, gdyby ktoś poprosił o wsparcie dla zardzewiałego żelaza.

Ale jeśli not piszesz czegoś, co musi działać wszędzie, to w chwili, gdy masz ochotę użyć jakiegokolwiek bashizmu, zamiast tego powinieneś przerwać i przepisać całą rzecz w lepszym języku skryptowym. Twoja przyszłość będzie ci wdzięczna.

(Kiedy więc należy używać rozszerzeń Bash? Do pierwszego rzędu: nigdy Do drugiego rzędu: tylko po to, aby rozszerzyć środowisko interaktywne Bash - np. Aby zapewnić inteligentne wypełnianie kart i fantazyjne monity.)

Related questions

Hot questions

Language

Popular Tags