Kiedy muszę użyć #! / Bin / bash, a kiedy #! / Bin / sh?

104

Kiedy jest #!/bin/bashbardziej odpowiednie niż #!/bin/shw skrypcie powłoki?

Hendré
źródło
55
Gdy używasz bashfunkcji i składni zamiast shfunkcji i składni.
Mokubai
6
Zobacz stackoverflow.com/questions/5725296/…
SPRBRN 10.10.16
3
Jeśli komukolwiek to pomaga, zauważyłem, że vimto podkreśli bash-izm, jeśli twój skrypt ma #!/bin/shshebang. Zmieniam to tylko bashwtedy, gdy robi się wystarczająco owłosiony, aby zacząć potrzebować funkcji bash.
SeldomNeedy,
@SeldomNeedy Niektóre z domyślnie podświetlanych elementów działają dobrze w dowolnej powłoce POSIX. $(...)jest szczególnie wstrętny. Ponadto niektóre z nich są subtelne [ <(...)i cmd >& filenie pojawiają się żadne błędy podświetlania, na przykład po prostu nie mają specjalnego wyróżnienia dla tego, co mają na myśli, z lub bez g:is_bash]
Random832
@ Random832 Wygląda na to, że ostatnio było trochę aktywności na ten temat w narzędziu do śledzenia problemów vima .
SeldomNeedy

Odpowiedzi:

149

W skrócie:

  • Istnieje kilka powłok, które implementują nadzbiór specyfikacji sh POSIX . W różnych systemach /bin/shmoże być link do ash, bash, dash, ksh, zsh i c. (Zawsze będzie jednak kompatybilny z sh - nigdy nie csh ani ryb.)

  • Tak długo, jak trzymasz się shtylko funkcji, możesz (a prawdopodobnie nawet powinien) używać, #!/bin/sha skrypt powinien działać dobrze, bez względu na to, która to powłoka.

  • Jeśli zaczniesz używać funkcji specyficznych dla basha (np. Tablic), powinieneś poprosić bash - ponieważ nawet jeśli /bin/shjuż wywołuje bash w twoim systemie, może nie być w systemie wszystkich innych i twój skrypt tam nie uruchomi. (To samo oczywiście dotyczy zsh i ksh.) Możesz użyć shellcheck, aby zidentyfikować bashisms.

  • Nawet jeśli skrypt jest tylko do użytku osobistego, możesz zauważyć, że niektóre systemy operacyjne zmieniają się /bin/shpodczas aktualizacji - np. W Debianie był bash, ale później został zastąpiony bardzo minimalnym myślnikiem. Skrypty, które używały bachizmów, ale #!/bin/shnagle się zepsuły.

Jednak:

  • Nawet #!/bin/bashnie jest bardzo poprawne. W różnych systemach bash może żyć w /usr/binlub /usr/pkg/binlub /usr/local/bin.

  • Bardziej niezawodną opcją jest #!/usr/bin/env bashużycie zmiennej PATH. (Chociaż envsamo narzędzie nie jest również ściśle gwarantowane, /usr/bin/envnadal działa na większej liczbie systemów niż /bin/bashdziała).

grawitacja
źródło
10
Niezła odpowiedź. Czy miałeś na myśli CW?
Mokubai
3
Tylko uwaga, jeśli uruchamiany jest bash, /bin/shwówczas spróbuje on naśladować shfunkcje (patrz strona podręcznika ), nie jestem pewien, czy w tym trybie dostępne są określone funkcje bash. envNarzędzie to jest narzędziem posiksowym, które należy znaleźć w większości dystrybucji, ale nie jestem pewien, ponieważ niektórzy mogą nie uszanować posix.
Brice,
8
Nie, w rzeczywistości POSIX wyraźnie stwierdza, że nie można go założyć /bin: „ Aplikacje powinny pamiętać, że nie można założyć, że standardową ścieżką do powłoki jest / bin / sh lub / usr / bin / sh, i należy ją określić przez zapytanie PATH zwrócone przez getconf PATH, upewniając się, że zwracana ścieżka jest absolutną ścieżką, a nie wbudowaną powłoką ". Zobacz także to pytanie, a konkretnie tę odpowiedź .
terdon
12
Hmm, cóż, więc to oznacza, że ​​POSIX nie definiuje przenośnego sposobu #!działania skryptów?
grawitacja 10.10.16
4
POSIX sh nie jest Bourne'em: jest bardziej pochodną wczesnego ksh niż bezpośrednim potomkiem Bourne'a. Wyróżnienie ich jest łatwe: echo foo ^ catemituje foo ^ catw POSIX sh i emituje tylko foow Bourne (podobnie jak ^znak potoku).
Charles Duffy,
18

Użyj shebang odpowiadającego powłoce, której faktycznie użyłeś do opracowania i debugowania skryptu. To znaczy bash, jeśli twoja powłoka logowania jest uruchomiona, a skrypt uruchamiasz się w terminalu jako wykonywalny, użyj #!/bin/bash. Nie zakładaj tylko, że skoro nie używałeś tablic (ani żadnej innej znanej bashCi funkcji), możesz bezpiecznie wybrać dowolną powłokę. Istnieje wiele subtelnych różnic między powłokami ( echo, funkcjami, pętlami, jak to nazywasz), których nie można wykryć bez odpowiedniego testowania.

Zastanów się: jeśli odejdziesz, #!/bin/basha twoi użytkownicy go nie mają, zobaczą wyraźny komunikat błędu, coś w rodzaju

Error: /bin/bash not found

Większość użytkowników może to naprawić w ciągu jednej minuty, instalując odpowiedni pakiet. Z drugiej strony, jeśli zastąpisz shebang przez #!/bin/shi przetestujesz go w systemie, w którym /bin/shznajduje się dowiązanie symboliczne /bin/bash, Twoi użytkownicy, którzy go nie mają, bashbędą mieli kłopoty. Najprawdopodobniej zobaczą tajemniczy komunikat o błędzie, taki jak:

Error in script.sh line 123: error parsing token xyz

Naprawienie tego może potrwać kilka godzin i nie będzie żadnych wskazówek, która powłoka powinna była użyć.

Nie ma wielu powodów, dla których chciałbyś użyć innej powłoki w shebang. Jednym z powodów jest to, że używana powłoka nie jest powszechna. Innym jest uzyskanie wydajności, shktóra jest znacznie szybsza w niektórych systemach, A twój skrypt będzie wąskim gardłem wydajności. W takim przypadku dokładnie przetestuj skrypt za pomocą docelowej powłoki, a następnie zmień shebang.

Dmitrij Grigoriew
źródło
@Hastur Nie dostaję twojego komentarza. Shebang z pewnością określi, która powłoka zostanie użyta do uruchomienia skryptu, czy myślisz, że moja odpowiedź sugeruje inaczej?
Dmitry Grigoryev
1
Zapomnij o tym. Źle zrozumiałem część „dlaczego chcesz używać” ...
Hastur,
6

Powinieneś tylko używać #! /bin/sh.

Nie powinieneś nigdy używać rozszerzeń bash (lub zsh, fish, lub ...) w skrypcie powłoki.

Powinieneś pisać tylko skrypty powłoki, które działają z dowolną implementacją języka powłoki (w tym ze wszystkimi programami „narzędziowymi”, które towarzyszą samej powłoce). W dzisiejszych czasach prawdopodobnie możesz uznać POSIX.1-2001 ( nie -2008) za wiarygodne dla tego, co potrafi powłoka i narzędzia, ale pamiętaj, że pewnego dnia możesz zostać poproszony o przeniesienie skryptu do starszego systemu (np. Solaris lub AIX), którego skorupa i narzędzia zostały zamrożone około 1992 r.

Poważnie ?!

Tak poważnie.

Oto rzecz: Shell to straszny język programowania. Jedyne, co ma do tego, to to, że /bin/shjest to jedyny interpreter skryptów, który gwarantuje każda instalacja uniksowa.

Oto druga rzecz: pewna iteracja podstawowego interpretera Perl 5 ( /usr/bin/perl) jest bardziej dostępna w losowo wybranej instalacji Unixa niż (/(usr|opt)(/(local|sfw|pkg)?)?/bin/bashjest. Inne dobre języki skryptowe (Python, Ruby, node.js itp. - W tej kategorii uwzględnię nawet PHP i Tcl w porównaniu z powłoką) są mniej więcej tak dostępne, jak bash i inne rozszerzone powłoki.

Dlatego jeśli masz opcję napisania skryptu bash, możesz zamiast tego użyć języka programowania, który nie jest straszny.

Teraz, proste skrypty powłoki, takie, które po prostu uruchamiają kilka programów w sekwencji z zadania crona lub coś takiego, nie ma nic złego w pozostawieniu ich jako skryptów powłoki. Ale proste skrypty powłoki nie potrzebują tablic ani funkcji, a [[nawet. Powinieneś pisać skomplikowane skrypty powłoki tylko wtedy, gdy nie masz innego wyboru. Na przykład skrypty Autoconf są nadal poprawnie skryptami powłoki. Ale te skrypty muszą być uruchamiane przy każdym wcieleniu /bin/shistotnym dla konfigurowanego programu. a to oznacza, że ​​nie mogą używać żadnych rozszerzeń. Prawdopodobnie w dzisiejszych czasach nie musisz przejmować się starymi zastrzeżonymi Uniksami, ale prawdopodobnie powinieneś przejmować się obecnymi BSD typu open source, z których niektóre nie instalują siębashdomyślnie oraz środowiska osadzone, które dają tylko minimalną powłokę i busybox.

Podsumowując, moment znaleźć się chcąc funkcję, która nie jest dostępna w języku powłoki przenośnej, że jest to znak, że skrypt stał się zbyt skomplikowany, aby zatrzymać skrypt. Zamiast tego przepisz go w lepszym języku.

zwol
źródło
4
Przepraszam, ale to trochę głupie. Tak, jeśli piszesz coś, co zostanie i) rozpowszechnione i ii) w różnych środowiskach, powinieneś trzymać się podstawowych sh. Jest to jednak dalekie od stwierdzenia, że nigdy nie powinieneś używać innych pocisków. Każdego dnia pisanych jest tysiące (prawdopodobnie o wiele więcej) skryptów, a zdecydowana większość z nich będzie działała tylko na jednym komputerze. Twoja rada ma sens w przypadku kodu produkcyjnego, ale nie dotyczy codziennych zadań „sysdaminy”, dla których napisano większość skryptów. Jeśli wiem, że mój skrypt będzie używany tylko przeze mnie i na komputerze z systemem Linux, bash jest w porządku.
terdon
1
@terdon Chodzi o to, że jeśli masz opcję napisania skryptu bash, to masz również możliwość napisania skryptu perl (lub cokolwiek innego), i zawsze jest to lepszy wybór.
zwol
@terdon Odpowiedź z pewnością nie jest głupia, twój komentarz jest po prostu naiwny. Skrypty powłoki są okropne do debugowania w porównaniu do Perla i tym podobnych, dla których można łatwo używać kompletnych IDE z graficznym debugowaniem i wszystkim innym. Ponadto większość skryptów pisanych każdego dnia dla drobiazgów ma tendencję do zmieniania i zmieniania od nowa, rozwoju, kopiowania jako przykładów dla kolegów lub sieci itp. Najprawdopodobniej nie ma powodu, aby podejmować takie ryzyko.
Thorsten Schöning
@ ThorstenSchöning powiedziałem trochę głupie. Gdyby to był Unix i Linux, a nawet Stack Overflow , mógłbym nawet się zgodzić. Jeśli mówimy o najlepszych praktykach ogólnych lub profesjonalnych programistach, oczywiście najlepiej trzymać się podstawowego POSIX sh. Jednak jest to Super User , strona skierowana do użytkowników końcowych i mówienie ludziom, aby nigdy nie używali bash ani zsh podczas pisania skryptów powłoki.
terdon
@terdon To jest rzeczywiście bardziej ważne, moim zdaniem, aby zniechęcić użytkowników końcowych od pisania złożonych skryptów powłoki. Specjaliści mają średnio wyższy poziom umiejętności (dlatego są bardziej prawdopodobne, że będą w stanie poradzić sobie z pułapkami złożonego skryptu powłoki), powinni z pewną precyzją wiedzieć, w których środowiskach muszą być przenośni, i otrzymują wynagrodzenie za nie poddawanie się w frustracji.
zwolnienie
4

Ogólnie, jeśli czas jest ważniejszy niż funkcjonalność, użyjesz szybszej powłoki. sh jest często aliasowany do myślnika i zwykle jest używany do zadań crona root lub operacji wsadowych, w których liczy się każda (nano) sekunda.

mckenzm
źródło
2
Załadowanie dodatkowego interpretera powłoki z systemu plików może zniweczyć taką przewagę, a wszystko, gdzie liczą się nanosekundy (które są tego samego rzędu wielkości co rzeczywisty cykl maszyny), jest domeną programistów C i programistów w asemblerze.
rackandboneman,
Nanosekundy robią różnicę w zadaniach crona tylko, jeśli masz ich miliardy, a cron i tak nie poradzi sobie z tak wieloma.
Dmitry Grigoryev
1
Nawet jeśli mckenzm trochę przesadził, nie można zaprzeczyć, że lepsza wydajność jest lepsza! Nie rozłączaj się w części „nano”. (Nanosekundy nawet się nie liczą w C, chyba że jest to wewnętrzna pętla w systemie czasu rzeczywistego itp.)
jpaugh,
1
@jpaugh Chyba że pracujesz z (starszym) systemem, który zakłada, że ​​pewne operacje potrwają co najmniej określony czas. Zdarza się!
JAB,
3

Mówiąc w skrócie, użyj, shjeśli najważniejsza jest przenośność w większości systemów i bashjeśli chcesz skorzystać z niektórych jej specyficznych funkcji, takich jak tablice, jeśli obsługuje ją wersja bash.

Spencer Williams
źródło
1
  • sh (w większości przypadków), bash, jeśli jest to szczególnie wymagane (lub ksh, lub cokolwiek innego)

Najbezpieczniejsza ścieżka. po zapisaniu na stałe będzie to / usr / bin /, shellOfChoiceale nowa konwencja, z której zawsze staram się korzystać - jako „domyślne lokalizacje” poprzez zmianę PATH może się zmienić:

#! / usr / bin / env sh lub
#! / usr / bin / env bash
lub np. skrypty perl
#! / usr / bin / env perl -w

Oczywiście, jeśli masz powód, aby skrypt NIGDY nie pobierał automatycznie nowej ŚCIEŻKI, to nadal powinien mieć zakodowany na stałe - i wtedy / usr / bin / coś powinno być najbardziej prawdopodobną ścieżką do użycia.

Michael Felt
źródło