In Ubuntu 16.04.3 habe ich ein sehr einfaches Bash-Skript:
test.sh
[[ 0 == 0 ]] && result="true" || result="false"
echo $result
echo $USER $SHELL $0
Wenn ich es als Nicht-Root-Benutzer me
oder als nenne root
, funktioniert es wie erwartet. Wenn ich benutze sudo ./test.sh
, beschwert es sich über einen Syntaxfehler:
$ ./test.sh
true
me /bin/bash ./test.sh
$ sudo su
# ./test.sh
true
root /bin/bash ./test.sh
# exit
$ sudo ./test.sh
./test.sh: 1: ./test.sh: [[: not found
false
root /bin/bash ./test.sh
Was könnte das verursachen? Wie kann ich das Problem beheben, damit me
dieses Skript sowohl normal als auch mit verwendet werden kann sudo
?
sudo su
. Einfach laufensudo -i
odersudo -s
stattdessen.sudo -i
ändert den Speicherort in/root
.sudo su
odersudo -s
ändern Sie nicht den Speicherort des Verzeichnisses.-s
.Antworten:
Jedes Skript beginnt mit einem Shebang , ohne ihn weiß die Shell, die Ihr Skript startet, nicht, welcher Interpreter Ihr Skript 1 ausführen soll, und kann es - wie im Fall von
sudo ./script.sh
hier - mit ausführen, mitsh
dem in Ubuntu 16.04 verknüpft istdash
. Der bedingte Ausdruck[[
ist einbash
zusammengesetzter Befehl ,dash
weiß also nicht, wie er damit umgehen soll, und löst den aufgetretenen Fehler aus.Die Lösung hier ist hinzuzufügen
als erste Zeile Ihres Skripts. Sie erhalten möglicherweise das gleiche Ergebnis, wenn Sie es explizit mit aufrufen
sudo bash ./script.sh
, aber ein Shebang ist der richtige Weg.Fügen Sie hinzu, welche Shell Ihr Skript ausführt
echo $0
. Das ist nicht dasselbe wieecho $SHELL
unter Berufung auf wiki.archlinux.org :1: Wie Sie begann
./test.sh
mitbash
ihm nur angenommenbash
, das gleiche gilt für densudo su
Sub - Shell.quelle
/bin/sh
.echo $0
Gibt mir den Namen des Skripts:./test.sh
)/proc/$$/exe
Punkte. Sie können auch verschiedene Variablen wie testen$BASH_VERSION
,$ZSH_VERSION
usw. (aber Strich stellt keine solche Variable)Wie @dessert erklärte , besteht das Problem hier darin, dass Ihr Skript keine Shebang-Zeile enthält . Ohne Shebang
sudo
wird standardmäßig versucht, die Datei mit auszuführen/bin/sh
. Ich konnte es nirgendwo dokumentiert finden, aber ich bestätigte es, indem ich densudo
Quellcode überprüfte, in dem ich Folgendes in der Datei fandpathnames.h
:Dies bedeutet "setzen, wenn die Variable
_PATH_BSHELL
nicht definiert ist, setzen Sie sie auf/bin/sh
". Dannconfigure
haben wir in dem Skript, das im Quell-Tarball enthalten ist:Diese Schleife wird sucht
/bin/bash
,/usr/bin/sh
,/sbin/sh
,/usr/sbin/sh
oder/bin/ksh
und dann setzt die_PATH_BSHELL
zu je nachdem was zuerst gefunden wurde . Da/bin/sh
war der erste in der Liste und es existiert,_PATH_BSHELL
ist auf gesetzt/bin/sh
. Das Ergebnis all dessen ist, dass die Standard-Shell von,sudo
sofern nicht anders definiert, lautet/bin/sh
.Also,
sudo
wird standardmäßig die Dinge zum Laufen mit/bin/sh
und auf Ubuntu, das eine symbolische Verknüpfung zu istdash
, ein minimal POSIX - konform Shell:Das
[[
Konstrukt ist eine Bash-Funktion, es ist nicht durch den POSIX-Standard definiert und wird nicht verstanden vondash
:Im Detail in den drei Aufrufen, die Sie versucht haben:
./test.sh
Nein
sudo
; Wenn keine Shebang-Zeile vorhanden ist, versucht Ihre Shell, die Datei selbst auszuführen. Da Sie ausgeführt werden,bash
wird dies effektiv ausgeführtbash ./test.sh
und funktioniert.sudo su
gefolgt von./test.sh
.Hier starten Sie eine neue Shell für den Benutzer
root
. Dies ist die Shell, die in der$SHELL
Umgebungsvariablen für diesen Benutzer definiert ist. Unter Ubuntu lautet die Standard-Shell von rootbash
:sudo ./test.sh
Hier können Sie
sudo
den Befehl direkt ausführen. Da die Standard-Shell/bin/sh
wie oben erläutert ist, führt dies dazu, dass das Skript ausgeführt wird. Dies/bin/sh
ist der Falldash
und schlägt fehl, dadash
dies nicht verstanden wird[[
.Hinweis : Die Details zum
sudo
Festlegen der Standard-Shell scheinen etwas komplexer zu sein. Ich habe versucht, die in meiner Antwort genannten Dateien so zu ändern, dass sie auf zeigen, war/bin/bash
abersudo
immer noch standardmäßig auf/bin/sh
. Es muss also einige andere Stellen im Quellcode geben, an denen die Standard-Shell definiert ist. Trotzdem bleibt der Hauptpunkt (dersudo
standardmäßig verwendet wirdsh
) bestehen.quelle
/bin/sh
aus der Fehlermeldung verwendet werden würde - was könnte es sonst noch möglich sein? Die Frage wird wunderbar beantwortet in Welche Shell verwendet sudo · SO , siehe auchman sudo
Abschnitt BEFEHLSAUSFÜHRUNG . Es stellt sich heraus,sudo
dass keine Zwischenschale verwendet wird !execve
Systemaufrufs, der standardmäßig verwendet wirdsh
. Und nein, Zwischen-Shells sind irrelevant. Hier geht es nicht um die Shell, die den Befehl ausführt, sondern um den Shell-Interpreter, der zum Lesen des angegebenen Shell-Skripts verwendet wird. Nein, es wird keine Zwischen-Shell gestartet, aber es wird weiterhin ein Shell-Interpreter für Shell-Skripte benötigt.