Bash-Skript: Unterschiedliche Ergebnisse beim Aufruf mit oder ohne Sudo


10

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 meoder 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 medieses Skript sowohl normal als auch mit verwendet werden kann sudo?


3
Profi-Tipp: Es macht keinen Sinn zu laufensudo su . Einfach laufen sudo -ioder sudo -sstattdessen.
— Terdon

@terdon sudo -iändert den Speicherort in /root. sudo suoder sudo -sändern Sie nicht den Speicherort des Verzeichnisses.
— James Newton

Ja, lesen Sie die Frage, auf die ich zuvor verlinkt habe, warum. Und sorry, ich habe meinen vorherigen Kommentar bearbeitet, den ich vergessen hatte zu erwähnen -s.
— Terdon

Antworten:


20

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.shhier - mit ausführen, mit shdem in Ubuntu 16.04 verknüpft ist dash. Der bedingte Ausdruck [[ ist ein bashzusammengesetzter Befehl , dashweiß also nicht, wie er damit umgehen soll, und löst den aufgetretenen Fehler aus.

Die Lösung hier ist hinzuzufügen

#!/bin/bash

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 wie echo $SHELL unter Berufung auf wiki.archlinux.org :

SHELL enthält den Pfad zur bevorzugten Shell des Benutzers. Beachten Sie, dass dies nicht unbedingt die aktuell ausgeführte Shell ist, obwohl Bash diese Variable beim Start festlegt.

1: Wie Sie begann ./test.shmit bashihm nur angenommen bash, das gleiche gilt für den sudo suSub - Shell.


1
Beachten Sie auch, dass bash ein Skript ohne shebang mit bash ausführt, nicht /bin/sh.
— Muru

@ Dessert Das behebt es. Vielen Dank! Wie kann ich in einem Skript überprüfen, auf welcher Shell es ausgeführt wird? ( echo $0Gibt mir den Namen des Skripts: ./test.sh)
— James Newton

@ JamesNewton kein tragbarer Weg, AFAIK, aber Sie können überprüfen, auf welche /proc/$$/exePunkte. Sie können auch verschiedene Variablen wie testen $BASH_VERSION, $ZSH_VERSIONusw. (aber Strich stellt keine solche Variable)
— muru

5

Wie @dessert erklärte , besteht das Problem hier darin, dass Ihr Skript keine Shebang-Zeile enthält . Ohne Shebang sudowird standardmäßig versucht, die Datei mit auszuführen /bin/sh. Ich konnte es nirgendwo dokumentiert finden, aber ich bestätigte es, indem ich den sudoQuellcode überprüfte, in dem ich Folgendes in der Datei fand pathnames.h:

#ifndef _PATH_BSHELL
#define _PATH_BSHELL "/bin/sh"
#endif /* _PATH_BSHELL */

Dies bedeutet "setzen, wenn die Variable _PATH_BSHELLnicht definiert ist, setzen Sie sie auf /bin/sh". Dann configurehaben wir in dem Skript, das im Quell-Tarball enthalten ist:

for p in "/bin/bash" "/usr/bin/sh" "/sbin/sh" "/usr/sbin/sh" "/bin/ksh" "/usr/bin/ksh" "/bin/bash" "/usr/bin/bash"; do
    if test -f "$p"; then
    found=yes
    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $p" >&5
$as_echo "$p" >&6; }
    cat >>confdefs.h <<EOF
#define _PATH_BSHELL "$p"
EOF

    break
    fi
done

Diese Schleife wird sucht /bin/bash, /usr/bin/sh, /sbin/sh, /usr/sbin/shoder /bin/kshund dann setzt die _PATH_BSHELLzu je nachdem was zuerst gefunden wurde . Da /bin/shwar der erste in der Liste und es existiert, _PATH_BSHELList auf gesetzt /bin/sh. Das Ergebnis all dessen ist, dass die Standard-Shell von, sudosofern nicht anders definiert, lautet /bin/sh.

Also, sudowird standardmäßig die Dinge zum Laufen mit /bin/shund auf Ubuntu, das eine symbolische Verknüpfung zu ist dash, ein minimal POSIX - konform Shell:

$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Feb 27  2015 /bin/sh -> dash

Das [[Konstrukt ist eine Bash-Funktion, es ist nicht durch den POSIX-Standard definiert und wird nicht verstanden von dash:

$ bash -c '[[ true ]] && echo yes'
yes
$ dash -c '[[ true ]] && echo yes'
dash: 1: [[: not found

Im Detail in den drei Aufrufen, die Sie versucht haben:

  1. ./test.sh

    Nein sudo; Wenn keine Shebang-Zeile vorhanden ist, versucht Ihre Shell, die Datei selbst auszuführen. Da Sie ausgeführt werden, bashwird dies effektiv ausgeführt bash ./test.shund funktioniert.

  2. sudo sugefolgt von ./test.sh.

    Hier starten Sie eine neue Shell für den Benutzer root. Dies ist die Shell, die in der $SHELLUmgebungsvariablen für diesen Benutzer definiert ist. Unter Ubuntu lautet die Standard-Shell von root bash:

    $ grep root /etc/passwd
    root:x:0:0:root:/root:/bin/bash
  3. sudo ./test.sh

    Hier können Sie sudoden Befehl direkt ausführen. Da die Standard-Shell /bin/shwie oben erläutert ist, führt dies dazu, dass das Skript ausgeführt wird. Dies /bin/shist der Fall dashund schlägt fehl, da dashdies nicht verstanden wird [[.


Hinweis : Die Details zum sudoFestlegen 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/bashaber sudoimmer 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 (der sudostandardmäßig verwendet wird sh) bestehen.


Ich konnte es nirgendwo dokumentiert finden - ich auch nicht, ich nahm nur an, dass es /bin/shaus 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 auch man sudoAbschnitt BEFEHLSAUSFÜHRUNG . Es stellt sich heraus, sudodass keine Zwischenschale verwendet wird !
— Dessert

1
@dessert ja, es verwendet eine eigene Implementierung des execveSystemaufrufs, der standardmäßig verwendet wird sh. 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.
— Terdon
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.