GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Warum unterbricht eine „sudo -i“-Login-Shell ein Here-doc-Befehlszeichenfolge-Argument?

In der Folge von fünf Befehlen unten hängen alle von einfachen Anführungszeichen ab, um mögliche Variablenersetzungen an die aufgerufene bash zu übergeben Shell statt der aufrufenden Shell. Der aufrufende Benutzer ist xx , aber die aufgerufene Shell wird als Benutzer yy ausgeführt . Der erste Befehl ersetzt $HOME durch den Wert der aufrufenden Shell, da die aufgerufene Shell keine Login-Shell ist. Der zweite Befehl ersetzt den Wert von $HOME, der von einer Login-Shell geladen wird, also ist es der Wert, der dem Benutzer yy gehört . Der dritte Befehl verlässt sich nicht auf einen $HOME-Wert und erstellt eine Datei im erratenen Home-Verzeichnis von Benutzer yy .

Warum schlägt der vierte Befehl fehl? Die Absicht ist, dass es dieselbe Datei schreibt, sich aber auf die $HOME-Variable verlässt, die dem Benutzer yy gehört um sicherzustellen, dass es tatsächlich in ihrem Home-Verzeichnis landet. Ich verstehe nicht, warum eine Login-Shell das Verhalten eines here-doc-Befehls unterbricht, der als statische Zeichenfolge in einfachen Anführungszeichen übergeben wird. Das Scheitern des fünften Befehls bestätigt, dass es sich bei diesem Problem nicht um die Variablensubstitution handelt.

[email protected] ~ $ sudo -u yy bash -c 'echo HOME=$HOME'
HOME=/home/xx
[email protected] ~ $ sudo -iu yy bash -c 'echo HOME=$HOME'
HOME=/home/yy
[email protected] ~ $ sudo -u yy bash -c 'cat > /home/yy/test.sh << "EOF"
> script-content
> EOF
> '
[email protected] ~ $ sudo -iu yy bash -c 'cat > $HOME/test.sh << "EOF"
> script-content
> EOF
> '
bash: warning: here-document at line 0 delimited by end-of-file (wanted `EOFscript-contentEOF')
[email protected] ~ $ sudo -iu yy bash -c 'cat > /home/yy/test.sh << "EOF"
> script-content
> EOF
> '
bash: warning: here-document at line 0 delimited by end-of-file (wanted `EOFscript-contentEOF')

Diese Befehle wurden auf einem Linux Mint 18.3 Cinnamon 64-Bit-System ausgegeben, das auf Ubuntu 16.04 (Xenial Xerus) basiert.

Aktualisierung: Der Here-Doc-Aspekt trübt das Problem nur. Hier ist eine Vereinfachung des Problems:

$ sudo bash -c 'echo 1
> echo 2'
1
2
$ sudo -i bash -c 'echo 1
> echo 2'
1echo 2

Warum behält der erste dieser beiden Befehle den Zeilenumbruch bei und der zweite nicht? sudo ist beiden Befehlen gemeinsam, scheint jedoch unterschiedlich zu maskieren/filtern/interpolieren, abhängig von nichts anderem als der Option „-i“.

Akzeptierte Antwort:

Die Dokumentation für -i Zustände:

Das -i (Anfangsanmeldung simulieren) führt die durch den Kennwortdatenbankeintrag des Zielbenutzers angegebene Shell als Anmelde-Shell aus. Dies bedeutet, dass Login-spezifische Ressourcendateien wie .profile oder .login von der Shell gelesen werden. Wenn ein Befehl angegeben ist, wird er zur Ausführung über die Option -c der Shell an die Shell übergeben.

Das heißt, es führt wirklich die Login-Shell des Benutzers aus und übergibt dann den Befehl, den Sie sudo gegeben haben dazu mit -cim Gegensatz was sudo cmd arg arg kommt normalerweise ohne das -i aus Möglichkeit. Normalerweise sudo verwendet einfach eine der exec* Funktionen direkt, um den Prozess selbst zu starten, ohne Zwischen-Shell und alle Argumente werden genau so wie sie sind durchgereicht.

Mit -i , es richtet die Umgebung ein, führt die Shell des Benutzers als Login-Shell aus und rekonstruiert den Befehl, den Sie als Argument für bash -c ausführen wollten . In Ihrem Fall läuft es (sagen wir ungefähr) /bin/bash -c "bash -c ' ... '" (Stellen Sie sich vor, das Zitieren funktioniert).

Das Problem liegt darin, wie sudo wandelt den von Ihnen geschriebenen Befehl in etwas um, das -c umgehen kann , erklärt im nächsten Abschnitt. Der letzte Abschnitt enthält einige mögliche Lösungen und dazwischen einige Debugging- und Verifizierungstechniken,

Warum passiert das?

Beim Übergeben des Befehls an -c , es braucht etwas Vorverarbeitung, damit es das Richtige tut, was sudo ist tut, bevor die Shell ausgeführt wird. Wenn Ihr Befehl beispielsweise lautet:

sudo -iu yy echo 'three   spaces'

dann müssen diese Leerzeichen maskiert werden, damit der Befehl dasselbe bedeutet (d. h. damit das einzelne Argument nicht in zwei Wörter aufgeteilt wird). Was am Ende ausgeführt wird, ist:

/bin/bash -c 'echo three   spaces'

Beginnen wir mit Ihrem vereinfachten Befehl:

sudo bash -c 'echo 1
echo 2'

In diesem Fall sudo wechselt den Benutzer und führt dann execvp("bash", ["bash", "-c", "echo 1necho 2"]) aus (für eine erfundene Array-Literal-Syntax).

Verwandte:Wie erhalte ich eine Bash-Vervollständigung für Befehlsaliase?

Mit -i :

sudo -i bash -c 'echo 1
echo 2'

stattdessen ändert es den Benutzer und führt dann execv("/bin/bash", ["-bash", "-c", "bash -c echo\ 1\necho\ 2"]) aus , wobei \ entspricht einem wörtlichen und n ist ein Zeilenumbruch. Es hat die Leerzeichen und den Zeilenumbruch in Ihrem Hauptbefehl maskiert, indem Sie ihnen Backslashes vorangestellt haben.

Das heißt, es gibt eine äußere Login-Shell, die zwei Argumente hat:-c und Ihr gesamter Befehl, rekonstruiert in eine Form, von der erwartet wird, dass sie die Shell korrekt versteht. Leider nicht. Die innere bash Befehl versucht schließlich auszuführen:

echo 1
echo 2

wobei die erste physische Zeile mit einer Zeilenfortsetzung (Backslash gefolgt von Newline) endet, die vollständig gelöscht wird. Die logische Zeile ist dann einfach echo 1echo 2 , was nicht das tut, was Sie wollten.

Es gibt ein Argument, dass dies ein Fehler in sudo ist 's maskiert, angesichts des Standardverhaltens von Backslash-Newline-Paaren. Ich denke, es sollte sicher sein, sie hier unverletzt zu lassen.

Das Gleiche gilt für Ihren Befehl mit einem Here-Dokument. Es läuft ungefähr so ​​ab:

/bin/bash -c 'bash -c cat << "EOF"\012script-content\012EOF\012'

wobei

Linux
  1. Warum verschlingt die Shell-Befehlsersetzung ein nachgestelltes Newline-Zeichen?

  2. Warum ruft popen() eine Shell auf, um einen Prozess auszuführen?

  3. Warum einen Linux-Shell-Befehl mit '&' ausführen?

  4. Warum sendet der Linux-Wall-Befehl kein String-Argument?

  5. Warum fügt Strg + V nicht in Bash (Linux-Shell) ein?

Linux chsh Command Tutorial für Anfänger (5 Beispiele)

Warum beginnt $shlvl in Nicht-Anmelde-Shells auf Stufe 2, in Anmelde-Shells in Rhel 7 jedoch auf Stufe 1?

Ändern der Standard-Shell in Linux

Was bedeutet die Syntax |&in der Shell-Sprache?

`ssh <host>` ist eine Login-Shell, aber `ssh <host> <command>` nicht?

Warum dauert die Ausführung des sudo-Befehls lange?