#! / bin / sh -
Jest (lub przynajmniej był) często zalecanym shebangiem do interpretacji skryptu /bin/sh
.
Dlaczego nie tylko #! /bin/sh
lub #!/bin/sh
?
Co to jest -
za?
#! / bin / sh -
Jest (lub przynajmniej był) często zalecanym shebangiem do interpretacji skryptu /bin/sh
.
Dlaczego nie tylko #! /bin/sh
lub #!/bin/sh
?
Co to jest -
za?
Odpowiedzi:
To z podobnego powodu, dla którego musisz pisać:
rm -- *.txt
I nie
rm *.txt
Chyba że możesz zagwarantować, że żaden .txt
plik w bieżącym katalogu nie ma nazwy zaczynającej się od -
.
W:
rm <arg>
<arg>
jest uważane za opcję, jeśli zaczyna się od -
lub plik do usunięcia w inny sposób. W
rm - <arg>
arg
jest zawsze uważany za plik do usunięcia, niezależnie od tego, czy zaczyna się od, -
czy nie.
To samo dotyczy sh
.
Kiedy wykonuje się skrypt, który zaczyna się od
#! /bin/sh
Zazwyczaj z:
execve("path/to/the-script", ["the-script", "arg"], [environ])
System przekształca go w:
execve("/bin/sh", ["/bin/sh", "path/to/the-script", "arg"], [environ])
path/to/the-script
Zwykle nie jest coś, co jest pod kontrolą autora skryptu. Autor nie jest w stanie przewidzieć, gdzie będą przechowywane kopie skryptu ani pod jaką nazwą. W szczególności nie mogą zagwarantować, path/to/the-script
że nazwa, z którą się nazywa, nie zacznie się -
(lub z +
którym jest również problem sh
). Dlatego trzeba-
tutaj, aby zaznaczyć koniec opcji.
Na przykład w moim systemie zcat
(podobnie jak większość innych skryptów) jest jeden przykład skryptu, który nie przestrzegał tego zalecenia:
$ head -n1 /bin/zcat
#!/bin/sh
$ mkdir +
$ ln -s /bin/zcat +/
$ +/zcat
/bin/sh: +/: invalid option
[...]
Teraz możesz zapytać dlaczego, #! /bin/sh -
a nie #! /bin/sh --
?
Chociaż #! /bin/sh --
działałby z powłokami POSIX, #! /bin/sh -
jest bardziej przenośny; w szczególności do starożytnych wersji sh
. sh
traktuje się -
jak i koniec opcji wcześniej getopt()
i ogólnie używa się --
do oznaczania końca opcji przez długi czas. Sposób, w jaki powłoka Bourne'a (z późnych lat 70.) przeanalizowała swoje argumenty, tylko pierwszy argument był rozważany dla opcji, jeśli zaczął -
. Wszystkie późniejsze znaki -
będą traktowane jako nazwy opcji; jeśli po znaku nie było żadnej postaci -
, nie było opcji. To utknęło i wszystkie późniejsze powłoki podobne do Bourne'a rozpoznają -
jako sposób na oznaczenie końca opcji.
W powłoce Bourne'a (ale nie we współczesnych powłokach podobnych do Bourne'a) #! /bin/sh -euf
również obejdzie ten problem, ponieważ rozważano tylko pierwszy argument dotyczący opcji.
Teraz można powiedzieć, że jesteśmy pedantyczni i dlatego też napisałem potrzebę kursywą powyżej:
-
lub +
lub umieścić je w katalogu, którego nazwa zaczyna się -
lub +
.execvp()
/ execlp()
-type. I w takim przypadku zazwyczaj wywołujesz je albo w the-script
celu wyszukania, w $PATH
którym to przypadku argument ścieżki do execve()
wywołania systemowego zwykle zaczyna się od /
(nie -
ani +
), albo tak ./the-script
, jakbyś chciał the-script
uruchomić w bieżącym katalogu (i wtedy ścieżka zaczyna się od ./
, -
ani +
też).Oprócz teoretycznej kwestii poprawności istnieje jeszcze jeden powód, dla którego #! /bin/sh -
został zalecony jako dobra praktyka . A to sięga czasów, kiedy kilka systemów nadal obsługiwało skrypty setuid.
Jeśli masz skrypt, który zawiera:
#! /bin/sh
/bin/echo "I'm running as root"
I ten skrypt był setuid root (jak z -r-sr-xr-x root bin
uprawnieniami), w tych systemach, gdy jest wykonywany przez zwykłego użytkownika,
execve("/bin/sh", ["/bin/sh", "path/to/the-script"], [environ])
byłoby zrobione jak root
!
Jeśli użytkownik utworzy dowiązanie symboliczne /tmp/-i -> path/to/the-script
i wykona je jako -i
, uruchomi interaktywną powłokę ( /bin/sh -i
) jako root
.
-
Byłoby obejść, że (nie byłoby obejść ten problem rasy warunek , albo fakt, że niektóre sh
implementacje jak jakiś ksh88
opartych o te byłyby Lookup argumentów skryptu bez /
w $PATH
choć).
W dzisiejszych czasach prawie żaden system nie obsługuje już skryptu setuid, a niektóre z tych, które nadal działają (zwykle nie są domyślnie), kończą robienie execve("/bin/sh", ["/bin/sh", "/dev/fd/<n>", arg])
(gdzie <n>
deskryptor pliku jest otwarty do odczytu na skrypcie), który działa zarówno na ten problem, jak i na warunki wyścigu.
Zauważ, że masz podobne problemy z większością tłumaczy, nie tylko z powłokami Bourne'a. Powłoki inne niż Bourne na ogół nie obsługują -
jako znacznika końca opcji, ale ogólnie obsługują --
zamiast tego (przynajmniej w przypadku nowoczesnych wersji).
Również
#! /usr/bin/awk -f
#! /usr/bin/sed -f
Nie ma problemu, gdy Kolejnym argumentem jest traktowane jako argument do -f
opcji w każdym razie, ale to nadal nie działa, jeśli path/to/script
jest -
(w tym przypadku, z większością sed
/ awk
implementacjach sed
/ awk
nie odczytać kod z -
plik, ale zamiast standardowego wejścia).
Pamiętaj również, że w większości systemów nie można używać:
#! /usr/bin/env sh -
#! /usr/bin/perl -w --
Podobnie jak w większości systemów, mechanizm shebang pozwala tylko na jeden argument za ścieżką tłumacza.
To, czy użyć #! /bin/sh -
vs #!/bin/sh -
, to tylko kwestia gustu. Wolę ten pierwszy, ponieważ sprawia, że ścieżka tłumacza jest bardziej widoczna i ułatwia wybór myszy. Istnieje legenda, która mówi, że przestrzeń była potrzebna w niektórych starożytnych wersjach Uniksa, ale AFAIK, która nigdy nie została zweryfikowana.
Bardzo dobre referencje na temat sznurka unixowego można znaleźć na stronie https://www.in-ulm.de/~mascheck/various/shebang
bash
akceptuje opcje jak każdą inną powłokę. Będąc powłoką podobną do Bourne'a i POSIX, bash
akceptuje zarówno #! /bin/bash -
i #! /bin/bash --
.