Ich schrieb ein Skript , das Benutzer schaltet beim Laufen, und ausgeführt es Dateiumleitung zu Standard zu verwenden. So user-switch.sh
ist ...
#!/bin/bash
whoami
sudo su -l root
whoami
Und bash
wenn ich es mit laufe, bekomme ich das Verhalten, das ich erwarte
$ bash < user-switch.sh
vagrant
root
Wenn ich das Skript jedoch mit ausführe sh
, erhalte ich eine andere Ausgabe
$ sh < user-switch.sh
vagrant
vagrant
Warum gibt es bash < user-switch.sh
eine andere Ausgabe als sh < user-switch.sh
?
Anmerkungen:
- passiert auf zwei verschiedenen Boxen, auf denen Debian Jessie läuft
bash
io-redirection
stdin
dash
popedotninja
quelle
quelle
sudo su
: unix.stackexchange.com/questions/218169/…/bin/sh
auf (meine Kopie von) Debian ist Dash, ja. Ich wusste bis jetzt nicht einmal, was das war. Ich bin bis jetzt bei Bash geblieben.user-switch.sh
in einem Jenkins-Job versucht und das Skript ausgeführt. Das Ausführen des gleichen Skripts außerhalb von Jenkins führte zu unterschiedlichen Ergebnissen, und ich griff auf die Dateiumleitung zurück, um das Verhalten zu erhalten, das ich in Jenkins gesehen habe.Antworten:
Ein ähnliches Skript ohne
sudo
, aber ähnliche Ergebnisse:Mit
bash
dem Rest des Skripts geht als Eingabesed
, mitdash
ihm, der Schale interpretiert.Laufen
strace
auf diesen:dash
Liest einen Block des Skripts (hier acht KB, mehr als genug, um das gesamte Skript zu speichern) und erzeugt dannsed
:Dies bedeutet, dass sich das Dateihandle am Ende der Datei befindet und
sed
keine Eingabe angezeigt wird . Der verbleibende Teil wird darin gepuffertdash
. (Wenn das Skript länger als die Blockgröße von 8 kB wäre, würde der verbleibende Teil von gelesensed
.)Bash hingegen sucht bis zum Ende des letzten Befehls zurück:
Wenn die Eingabe von einer Pipe stammt, wie hier:
Das Zurückspulen ist nicht möglich, da Rohre und Muffen nicht suchbar sind. In diesem Fall fällt Bash zurück , um die Eingabe zu lesen ein Zeichen zu einem Zeitpunkt zu vermeiden overreading. (
fd_to_buffered_stream()
ininput.c
) Ein vollständiger Systemaufruf für jedes Byte ist im Prinzip nicht sehr effektiv. In der Praxis denke ich nicht, dass das Lesen ein großer Aufwand sein wird, z. B. im Vergleich zu der Tatsache, dass die meisten Dinge, die die Shell tut, das Laichen ganz neuer Prozesse beinhalten.Eine ähnliche Situation ist folgende:
Die Subshell muss sicherstellen, dass
read
nur die erste neuehead
Zeile gelesen wird, damit die nächste Zeile angezeigt wird . (Dies funktioniert auch mitdash
.)Mit anderen Worten, Bash unternimmt zusätzliche Anstrengungen, um das Lesen derselben Quelle für das Skript selbst und für von ihm ausgeführte Befehle zu unterstützen.
dash
nicht. Diezsh
undksh93
in Debian verpackten gehen mit Bash dazu.quelle
strace
später ausführen und die Ausgaben vergleichen. Ich hatte diesen Ansatz nicht gewählt.Die Shell liest das Skript von der Standardeingabe. Innerhalb des Skripts führen Sie einen Befehl aus, der auch die Standardeingabe lesen möchte. Welcher Eingang wird wohin gehen? Sie können nicht zuverlässig sagen .
Shells funktionieren so, dass sie einen Teil des Quellcodes lesen, analysieren und einen vollständigen Befehl ausführen, den Befehl ausführen und dann mit dem Rest des Teils und dem Rest der Datei fortfahren. Wenn der Block keinen vollständigen Befehl enthält (mit einem abschließenden Zeichen am Ende - ich denke, alle Shells lesen bis zum Ende einer Zeile), liest die Shell einen weiteren Block und so weiter.
Wenn ein Befehl im Skript versucht, aus demselben Dateideskriptor zu lesen, aus dem die Shell das Skript liest, findet der Befehl alles, was nach dem letzten gelesenen Block kommt. Dieser Speicherort ist unvorhersehbar: Dies hängt von der ausgewählten Blockgröße der Shell ab. Dies kann nicht nur von der Shell und ihrer Version abhängen, sondern auch von der Maschinenkonfiguration, dem verfügbaren Speicher usw.
Bash versucht, das Ende des Quellcodes eines Befehls im Skript zu erreichen, bevor der Befehl ausgeführt wird. Darauf können Sie sich nicht verlassen, nicht nur, weil andere Shells dies nicht tun, sondern auch, weil dies nur funktioniert, wenn die Shell aus einer regulären Datei liest. Wenn die Shell aus einer Pipe liest (z. B.
ssh remote-host.example.com <local-script-file.sh
), werden gelesene Daten gelesen und können nicht ungelesen werden.Wenn Sie Eingaben an einen Befehl im Skript übergeben möchten, müssen Sie dies explizit tun, normalerweise mit einem Here-Dokument . (Ein Dokument hier ist normalerweise am bequemsten für mehrzeilige Eingaben, aber jede Methode reicht aus.) Der von Ihnen geschriebene Code funktioniert nur in wenigen Shells, nur wenn das Skript als Eingabe aus einer regulären Datei an die Shell übergeben wird. Wenn Sie erwartet haben, dass die Sekunde
whoami
als Eingabe an übergeben wirdsudo …
, denken Sie noch einmal darüber nach, dass das Skript die meiste Zeit nicht an die Standardeingabe der Shell übergeben wird.Beachten Sie, dass Sie dieses Jahrzehnt verwenden können
sudo -i root
. Laufensudo su
ist ein Hack aus der Vergangenheit.quelle