Który interpreter powłoki uruchamia skrypt bez shebang?


17

Załóżmy, że domyślną powłoką dla mojego konta jest zsh, ale otworzyłem terminal i odpaliłem bash i wykonałem skrypt o nazwie prac002.sh, który interpreter powłoki będzie użyty do wykonania skryptu, zsh czy bash? Rozważ następujący przykład:

papagolf@Sierra ~/My Files/My Programs/Learning/Shell % sudo cat /etc/passwd | grep papagolf
[sudo] password for papagolf: 
papagolf:x:1000:1001:Rex,,,:/home/papagolf:/usr/bin/zsh
# papagolf's default shell is zsh

papagolf@Sierra ~/My Files/My Programs/Learning/Shell % bash
# I fired up bash. (See that '%' prompt in zsh changes to '$' prompt, indicating bash.)

papagolf@Sierra:~/My Files/My Programs/Learning/Shell$ ./prac002.sh 
Enter username : Rex
Rex
# Which interpreter did it just use?

** EDYCJA: ** Oto treść skryptu

papagolf@Sierra ~/My Files/My Programs/Learning/Shell % cat ./prac002.sh 
read -p "Enter username : " uname
echo $uname

Co mówi skrypt na górze?
Michael Homer,

@MichaelHomer: Nic. Właśnie edytowałem pytanie, aby dodać treść skryptu. Skrypt ma uprawnienia do wykonywania.
7_R3X

Jeśli chcesz użyć bieżącej powłoki, . prac002.shumieść ją w źródle, jak w , zakładając, że twój skrypt znajduje się w bieżącym katalogu.
thecarpy

1
Uruchom, . ./prac002.sha uruchomi się z bieżącą powłoką, tj. Kropką ( .), spacją (), a następnie ścieżką skryptu. Nazywa się to zbieraniem kropek w skrypcie. ;-)
thecarpy

Odpowiedzi:


19

Ponieważ skrypt nie zaczyna się od #!linii shebang wskazującej, którego interpretera należy użyć, POSIX mówi, że :

Jeżeli execl()funkcja zawiedzie z powodu błędu równoważnego błędowi [ENOEXEC] zdefiniowanemu w woluminie interfejsów systemowych POSIX.1-2008, powłoka wykonuje polecenie równoważne z wywołaniem powłoki z nazwą ścieżki wynikającą z wyszukiwania jako pierwszą operand , z pozostałymi argumentami przekazanymi do nowej powłoki, z wyjątkiem tego, że wartość „$ 0” w nowej powłoce może być ustawiona na nazwę polecenia. Jeśli plik wykonywalny nie jest plikiem tekstowym, powłoka może ominąć wykonanie tego polecenia. W takim przypadku wypisze komunikat o błędzie i zwróci status wyjścia równy 126.

Frazowanie jest trochę niejednoznaczne, a różne powłoki mają różne interpretacje.

W takim przypadku Bash uruchomi skrypt za pomocą samego siebie . Z drugiej strony, jeśli zamiast tego uruchomiłeś go z zsh, zsh użyłbysh (cokolwiek, co jest w twoim systemie).

Możesz sprawdzić to zachowanie w tym przypadku, dodając następujące wiersze do skryptu:

echo $BASH_VERSION
echo $ZSH_VERSION

Zauważysz, że w Bash pierwszy wiersz wypisuje twoją wersję, podczas gdy drugi nigdy nic nie mówi, bez względu na to, jakiej powłoki używasz.

  • Jeśli twój /bin/sh, powiedzmy, dashżadna linia nie wyświetli niczego, gdy skrypt jest wykonywany z zsh lub dash.
  • Jeśli twój /bin/shlink do Bash, zobaczysz wyjście pierwszego wiersza we wszystkich przypadkach.
  • Jeśli /bin/shjest to inna wersja Bash niż bezpośrednio, zobaczysz inne dane wyjściowe po uruchomieniu skryptu bezpośrednio z bash i zsh.

ps -p $$Polecenie od odpowiedzi rools koszulka pokaże także przydatnych informacji na temat polecenia powłoki używanej do wykonywania skryptu.


Zwróciłem jednak uwagę, że używa ona USTAWIENIA DOMYŚLNEGO, jeśli nie jest określony shebang, niezależnie od domyślnej powłoki, którą określiłeś. Dlatego zawsze powinieneś, imho, określać shebang.
thecarpy

4
Tak nie jest, co tak naprawdę stanowiło sedno odpowiedzi. Twoje drugie zdanie jest z pewnością prawdziwe.
Michael Homer

masz rację, bash uruchamia go za pomocą samego siebie, domyślną powłoką na większości moich pudełek jest… bash, stamtąd moje zamieszanie. Właśnie przetestowałem na Solarisie 8 z domyślną powłoką ksh, oczywiście, bash uruchamia go jako bash ... dowiedziałem się dzisiaj czegoś nowego, dzięki (głosowałem!) ;-)
thecarpy

Dzięki. Czy niepowodzenie execl()wywołania występuje, gdy skrypt powłoki nie zawiera shebang i jest wykonywany jako scriptname? Czy to się nie zdarza, gdy skrypt powłoki jest wykonywany jako bash scriptname? Czy to się nie zdarza, gdy skrypt powłoki zawiera shebang i jest wykonywany jako scriptname?
StackExchange dla wszystkich


7

Ponieważ plik nie należy do żadnego z typów plików wykonywalnych rozpoznawanych przez system i przy założeniu, że masz uprawnienia do wykonania tego pliku, execve()wywołanie systemowe zwykle kończy się niepowodzeniem z błędem ENOEXEC( nie wykonywalnym ).

To, co się wtedy stanie, zależy od aplikacji i / lub funkcji bibliotecznej użytej do wykonania polecenia.

Może to być na przykład powłoka, funkcja execlp()/ execvp()libc.

Większość innych aplikacji będzie używać jednej z tych aplikacji po uruchomieniu polecenia. Będą wywoływać powłokę na przykład za pomocą system("command line")funkcji libc, która zwykle wywołuje w shcelu parsowania tej linii poleceń (której ścieżkę można określić w czasie kompilacji (jak /bin/shvs /usr/xpg4/bin/shw Solarisie)) lub wywołuje powłokę przechowywaną $SHELLprzez siebie jak viz jego !poleceniem lub xterm -e 'command line'wieloma innymi poleceniami ( su user -czamiast tego wywoła powłokę logowania użytkownika $SHELL).

Zasadniczo plik tekstowy bez shebang, który się nie zaczyna, #jest uważany za shskrypt. Które shto będzie się różnić.

execlp()/ execvp(), po execve()powrocie ENOEXECzwykle wywołuje shna nim. W przypadku systemów, które mają więcej niż jeden, shponieważ mogą być zgodne z więcej niż jednym standardem, co shzwykle określa się w czasie kompilacji (aplikacji używającej execvp()/ execlp()przez połączenie innego obiektu blob kodu, który odnosi się do innej ścieżki sh). Na przykład w /usr/xpg4/bin/shsystemie Solaris będzie to (standard, POSIX sh) lub /bin/sh(powłoka Bourne'a (przestarzała powłoka) w systemie Solaris 10 i starszych wersjach, ksh93 w systemie Solaris 11).

Jeśli chodzi o muszle, istnieje wiele odmian. bash, AT&T ksh, powłoka Bourne'a zazwyczaj interpretuje sam skrypt (w procesie potomnym, o ile nie execjest używany) po symulacji a execve(), czyli nieuzbrojonej wszystkich nieobsługiwanych zmiennych, zamykając wszystkie fds close-on-exec, usuwając wszystkie niestandardowe pułapki, aliasy, funkcje ... ( bashzinterpretuje skrypt w shtrybie). yashwykona się ( tak shjak argv[0]w shtrybie), aby go zinterpretować.

zsh, pdksh, ashOpartych powłoki zazwyczaj wywołania sh(ścieżka, która określona w czasie kompilacji).

Dla cshi tcsh(i shniektórych wczesnych BSD), jeśli pierwszym znakiem pliku jest #, to wykonają się, aby go zinterpretować, i w shprzeciwnym razie. To sięga czasów sprzed shebang, w których cshrozpoznawano #jako komentarze, ale nie powłokę Bourne'a, więc #była to wskazówka, że ​​był to skrypt csh.

fish(przynajmniej wersja 2.4.0), po prostu zwraca błąd, jeśli się execve()nie powiedzie (nie próbuje traktować go jako skryptu).

Niektóre powłoki (jak bashAT&T ksh) najpierw spróbują heurystycznie ustalić, czy plik prawdopodobnie ma być skryptem, czy nie. Może się więc okazać, że niektóre powłoki odmówią wykonania skryptu, jeśli w pierwszych kilku bajtach ma znak NUL.

Zauważ też, że jeśli execve()nie powiedzie się ENOEXEC, ale plik ma linię shebang, niektóre powłoki próbują sami interpretować tę linię shebang.

Oto kilka przykładów:

  • Kiedy $SHELLjest /bin/bash, xterm -e 'myscript with args'będzie myscriptinterpretowane przez bashw shtrybie. Podczas gdy xterm -e myscript with args, xtermużyje, execvp()więc skrypt zostanie zinterpretowany przez sh.
  • su -c myscriptna Solarisie 10, gdzie rootpowłoka logowania jest /bin/shi /bin/shjest powłoką Bourne'a, zostanie myscriptzinterpretowana przez powłokę Bourne'a.
  • /usr/xpg4/bin/awk 'BEGIN{system("myscript")'na Solarisie 10 będzie interpretowany przez /usr/xpg4/bin/sh(to samo dla /usr/xpg4/bin/env myscript).
  • find . -prune -exec myscript {} \;na Solarisie 10 (używanie execvp()) będzie interpretowane /bin/shnawet przez /usr/xpg4/bin/find, nawet w środowisku POSIX (błąd zgodności).
  • csh -c myscriptbędzie interpretowany przez, cshjeśli zacznie od #, w shprzeciwnym razie.

Podsumowując, nie możesz być pewien, która powłoka zostanie użyta do interpretacji tego skryptu, jeśli nie wiesz jak i przez co zostanie on wywołany.

W każdym razie read -pjest to bashtylko składnia, więc upewnij się, że skrypt jest interpretowany przez bash(i unikaj tego mylącego .shrozszerzenia). Albo znasz ścieżkę bashpliku wykonywalnego i użyj:

#! /path/to/bash -
read -p ...

Lub możesz polegać na $PATHwyszukiwaniu bashpliku wykonywalnego (zakładając, że bashjest zainstalowany), używając:

#! /usr/bin/env bash
read -p ...

( envjest prawie wszechobecny w /usr/bin). Alternatywnie możesz ustawić kompatybilność POSIX + Bourne. W takim przypadku możesz użyć /bin/sh. Wszystkie systemy będą miały /bin/sh. W większości z nich będzie (w przeważającej części) kompatybilny z POSIX, ale możesz od czasu do czasu znaleźć tam powłokę Bourne'a.

#! /bin/sh -
printf >&2 'Enter a user name: '
read user
printf '%s\n' "$user"

1

Gdy nie masz żadnej linii #!(zwanej shebang ), używa się sh . Aby to sprawdzić, możesz uruchomić następujący skrypt.

ps -p $$
echo -n "The real shell is: "
realpath /proc/$$/exe

Na moim komputerze dostaję

  PID TTY          TIME CMD
13718 pts/16   00:00:00 sh
The real program is: /usr/bin/bash

nawet jeśli moją domyślną powłoką jest zsh . Używa bash, ponieważ na moim komputerze polecenie sh jest implementowane przez bash .


1
Dlaczego to głosowałeś? Wyjaśnij, czy to jest bezużyteczne ...
rools

Hejterzy (milczący anonimowi downvoters) będą nienawidzić. Nauczyłem się czegoś z twojej odpowiedzi, więc oto głosowanie. :-)
Mac
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.