Wie funktioniert der Trick "Schreiben mit Sudo"?

1411

Viele von Ihnen haben wahrscheinlich den Befehl gesehen, mit dem Sie in eine Datei schreiben können, für die Root-Berechtigungen erforderlich sind, auch wenn Sie vergessen haben, vim mit sudo zu öffnen:

:w !sudo tee %

Die Sache ist, dass ich nicht verstehe, was genau hier passiert.

Ich habe mir das schon gedacht: wist dafür

                                                        *:w_c* *:write_c*
:[range]w[rite] [++opt] !{cmd}
                        Execute {cmd} with [range] lines as standard input
                        (note the space in front of the '!').  {cmd} is
                        executed like with ":!{cmd}", any '!' is replaced with
                        the previous command |:!|.

Daher werden alle Zeilen als Standardeingabe übergeben.

Der !sudo teeTeil ruft teemit Administratorrechten auf.

Damit alle Sinn machen, %sollte der Dateiname (als Parameter für tee) ausgegeben werden , aber ich kann keine Referenzen in der Hilfe für dieses Verhalten finden.

tl; dr Könnte mir jemand helfen sezieren diesen Befehl?

Doppelgänger
quelle
5
@ Nathan: Würde :w !sudo cat > %nicht so gut funktionieren und die Standardausgabe nicht verschmutzen?
Bjarke Freund-Hansen
51
@bjarkef - nein, das funktioniert nicht. In diesem Fall sudowird auf angewendet cat, aber nicht auf >, daher ist dies nicht zulässig. Sie könnten versuchen, den gesamten Befehl in einer sudo-Subshell auszuführen, :w !sudo sh -c "cat % > yams.txt"aber das funktioniert auch nicht, da in der Subshell %null ist. Sie werden den Inhalt Ihrer Datei ausblenden.
Nathan Long
3
Ich möchte nur hinzufügen, dass nach Eingabe dieses Befehls möglicherweise eine Warnmeldung angezeigt wird. Wenn ja, drücken Sie L. Dann werden Sie aufgefordert, die Eingabetaste zu drücken. Wenn Sie dies tun, wird Ihre Datei endlich gespeichert.
Pablofiumara
9
@NathanLong @knittl: Funktioniert :w !sudo sh -c "cat >%"tatsächlich genauso gut, sudo tee %weil Vim den Dateinamen ersetzt, %bevor er jemals in die Subshell gelangt. Keiner von ihnen funktioniert jedoch, wenn der Dateiname Leerzeichen enthält. Sie müssen das tun :w !sudo sh -c "cat >'%'"oder :w !sudo tee "%"beheben.
Han Seoul-Oh
1
Speichern Sie mit: W und laden Sie die Datei neu: Befehl W: Ausführen ': Silent w! Sudo tee%> / dev / null' | :bearbeiten!
Diego Roberto Dos Santos

Antworten:

1611

In :w !sudo tee %...

% bedeutet "die aktuelle Datei"

Wie eugene y hervorhob , %bedeutet dies in der Tat "den aktuellen Dateinamen", der übergeben wird, teedamit er weiß, welche Datei überschrieben werden muss.

(Bei Substitutionsbefehlen ist dies etwas anders. Wie :help :%gezeigt, ist dies der equal to 1,$ (the entire file)Fall (danke an @Orafu für den Hinweis, dass dies nicht als Dateiname ausgewertet wird).:%s/foo/bar " in der aktuellen Datei Vorkommen von foodurch ersetzen bar". Wenn Sie markieren Wenn :sSie vor dem Eingeben Text eingeben , werden Sie feststellen, dass die hervorgehobenen Zeilen den Platz von einnehmen% Ihres Substitutionsbereichs .)

:w aktualisiert Ihre Datei nicht

Ein verwirrender Teil dieses Tricks ist, dass Sie vielleicht denken :w Ihre Datei ändern, aber das ist es nicht. Wenn Sie geöffnet und geändert file1.txtund dann ausgeführt haben :w file2.txt, ist dies ein "Speichern unter". file1.txtwürde nicht geändert, aber der aktuelle Pufferinhalt würde an gesendet file2.txt.

Anstatt file2.txt können Sie einen Shell-Befehl ersetzen, um den Pufferinhalt zu erhalten . Zum Beispiel,:w !cat wird nur der Inhalt angezeigt.

Wenn Vim nicht mit sudo-Zugriff ausgeführt wurde, :wkann eine geschützte Datei nicht geändert werden. Wenn der Pufferinhalt jedoch an die Shell übergeben wird, wird ein Befehl in der Shell ausgeführt kann mit sudo ausgeführt werden . In diesem Fall verwenden wirtee.

Tee verstehen

Was tee, Bild , um dietee Befehls als ein T-förmiges Rohr in einer normalen Situation bash Verrohrung: es Ausgang angegebener Datei (en) leitet , und sendet es auch an der Standardausgabe , die durch den nächsten Befehl verrohrt erfaßt werden kann.

In wird beispielsweise ps -ax | tee processes.txt | grep 'foo'die Liste der Prozesse in eine Textdatei geschrieben und an weitergeleitet grep.

     +-----------+    tee     +------------+
     |           |  --------  |            |
     | ps -ax    |  --------  | grep 'foo' |
     |           |     ||     |            |
     +-----------+     ||     +------------+
                       ||   
               +---------------+
               |               |
               | processes.txt |
               |               |
               +---------------+

(Mit Asciiflow erstelltes Diagramm .)

Weitere Informationen finden Sie auf der teeManpage .

Tee als Hack

In der Situation, die Ihre Frage beschreibt, ist die Verwendung teeein Hack, da wir die Hälfte dessen ignorieren, was sie bewirkt . sudo teeschreibt in unsere Datei und sendet auch den Pufferinhalt an die Standardausgabe, aber wir ignorieren die Standardausgabe . In diesem Fall müssen wir nichts an einen anderen Pipeline-Befehl übergeben. Wir verwenden nur teeeine alternative Methode zum Schreiben einer Datei, damit wir sie aufrufen können sudo.

Diesen Trick einfach machen

Sie können dies zu Ihrem hinzufügen .vimrc, um diesen Trick benutzerfreundlich zu machen: Geben Sie einfach ein :w!!.

" Allow saving of files as sudo when I forgot to start vim using sudo.
cmap w!! w !sudo tee > /dev/null %

Der > /dev/nullTeil wirft die Standardausgabe explizit weg, da wir, wie gesagt, nichts an einen anderen Pipeline-Befehl übergeben müssen.

Nathan Long
quelle
147
Besonders wie deine Notation "w !!" Das ist so leicht zu merken, nachdem man "sudo !!" in der Kommandozeile.
Aidan Kane
11
Dies nutzt also teeseine Fähigkeit, stdin in eine Datei zu schreiben. Ich bin überrascht, dass es kein Programm gibt, dessen Aufgabe es ist, das zu tun (ich habe ein Programm gefunden, von dem ich noch nie gehört habe, spongedas dies tut). Ich denke, das typische "Schreiben eines Streams in eine Datei" wird von einer eingebauten Shell ausgeführt. Gabelt Vim !{cmd}keine Muschel ( cmdstattdessen Gabeln )? Vielleicht ist etwas Offensichtlicheres, eine funktionierende Variante von sh -c ">"anstatt zu verwenden tee.
Steven Lu
2
@Steven Lu: spongeist Teil des moreutilsPakets für so ziemlich jede Distribution außer Debian-basierten Distributionen. moreutilshat einige ziemlich nette Werkzeuge, die mit allgemeineren Werkzeugen wie xargsund vergleichbar sind tee.
Schweizer
10
Wie kann man diesen Alias ​​erweitern, um vim anzuweisen, den geänderten Dateiinhalt automatisch in den aktuellen Puffer zu laden? Es fragt mich, wie ich es automatisieren soll.
Zlatko
10
@ user247077: In diesem Fall wird catals root ausgeführt und die Ausgabe wird von der Shell umgeleitet, die nicht als root ausgeführt wird. Es ist das gleiche wie echo hi > /read/only/file.
WhyNotHugo
99

%Steht in der ausgeführten Befehlszeile für den aktuellen Dateinamen . Dies ist dokumentiert in :help cmdline-special:

In Ex commands, at places where a file name can be used, the following
characters have a special meaning.
        %       Is replaced with the current file name.

Wie Sie bereits herausgefunden haben, :w !cmdleitet der Inhalt des aktuellen Puffers an einen anderen Befehl weiter. Was teetut , ist die Standardeingabe in einer oder mehr Dateien zu kopieren, und auch auf der Standardausgabe. :w !sudo tee % > /dev/nullSchreibt daher effektiv den Inhalt des aktuellen Puffers in die aktuelle Datei, während Sie root sind . Ein weiterer Befehl, der dafür verwendet werden kann, ist dd:

:w !sudo dd of=% > /dev/null

Als Verknüpfung können Sie diese Zuordnung zu Ihrem hinzufügen .vimrc:

" Force saving files that require root permission 
cnoremap w!! w !sudo tee > /dev/null %

Mit dem oben genannten können Sie eingeben :w!!<Enter>, um die Datei als root zu speichern.

Eugene Yarmash
quelle
1
Interessant, zeigt an :help _%, was Sie eingegeben haben, zeigt aber :help %den Klammer-Matching-Schlüssel an. Ich hätte nicht gedacht, das Unterstrichpräfix zu versuchen. Ist das ein Muster in der vim-Dokumentation? Gibt es noch andere "besondere" Dinge, die Sie bei der Suche nach Hilfe ausprobieren sollten?
David Pope
2
@ David: Der helpBefehl springt zu einem Tag. Sie können verfügbare Tags mit anzeigen :h help-tags. Sie können auch die Befehlszeilenvervollständigung verwenden, um übereinstimmende Tags :h cmdline<Ctrl-D>:h cmdline<Tab>wildmode
anzuzeigen
1
Ich musste cmap w!! w !sudo tee % > /dev/nullin meiner .vimrc-Datei verwenden, damit dies funktioniert. Ist das %in der obigen Antwort falsch platziert? (Kein vim Experte hier.)
dmmfll
@ DMfll Ja, das ist es. Der Befehl in der Antwort würde dazu führen, sudo tee > /dev/null /path/to/current/filedass dies nicht wirklich Sinn macht. (
Ich werde das
1
@jazzpi: Du liegst falsch. Shells ist es eigentlich egal, wo in der Befehlszeile Sie die Dateiumleitung durchführen.
Eugene Yarmash
19

Das funktioniert auch gut:

:w !sudo sh -c "cat > %"

Dies ist inspiriert von dem Kommentar von @Nathan Long.

HINWEIS :

"muss verwendet werden, anstatt 'weil wir erweitert werden möchten %, bevor wir zur Shell übergehen.

feihu
quelle
11
Dies mag zwar funktionieren, bietet Sudo aber auch Zugriff auf mehrere Programme (sh und cat). Die anderen Beispiele könnten sicherer sein, indem sie durch ersetzt teewerden /usr/bin/tee, um Angriffe mit PATH-Modifikation zu verhindern.
idbrii
18

:w - Schreiben Sie eine Datei.

!sudo - Rufen Sie den Befehl shell sudo auf.

tee- Die Ausgabe des Befehls write (vim: w) wird mit tee umgeleitet. Das% ist nichts anderes als der aktuelle Dateiname, dh /etc/apache2/conf.d/mediawiki.conf. Mit anderen Worten, der Befehl tee wird als root ausgeführt und nimmt die Standardeingabe entgegen und schreibt sie in eine durch% dargestellte Datei. Dies führt jedoch dazu, dass die Datei erneut geladen wird (drücken Sie L, um Änderungen in vim selbst zu laden):

Tutorial Link

kev
quelle
9

Die akzeptierte Antwort deckt alles ab, daher gebe ich nur ein weiteres Beispiel für eine Verknüpfung , die ich für die Aufzeichnung verwende.

Fügen Sie es Ihrem etc/vim/vimrc(oder ~/.vimrc) hinzu:

  • cnoremap w!! execute 'silent! write !sudo tee % >/dev/null' <bar> edit!

Wo:

  • cnoremap: erzählt vim dass die folgende Verknüpfung in der Befehlszeile zugeordnet werden soll.
  • w!!: die Verknüpfung selbst.
  • execute '...': Ein Befehl, der die folgende Zeichenfolge ausführt.
  • silent!: Lass es leise laufen
  • write !sudo tee % >/dev/null: Die OP-Frage hat eine Umleitung von Nachrichten zu hinzugefügt NULL , um einen sauberen Befehl auszuführen
  • <bar> edit!: Dieser Trick ist die Kirsche des Kuchens: Er ruft auch den editBefehl auf, den Puffer neu zu laden und dann zu vermeiden, dass sich Nachrichten wie der Puffer geändert haben . <bar>Hier erfahren Sie, wie Sie das Pipe- Symbol schreiben , um zwei Befehle zu trennen.

Ich hoffe es hilft. Siehe auch für andere Probleme:

Dr. Beco
quelle
Leise! deaktiviert die Passwort-Eingabeaufforderung, so dass Sie es nicht sehen
Andy Ray
7

Ich möchte einen anderen Ansatz für die "Oups, die ich sudobeim Öffnen meiner Datei vergessen habe zu schreiben" vorschlagen. :

Anstatt ein zu empfangen permission deniedund eingeben zu müssen :w!!, finde ich es eleganter, einen bedingten vimBefehl zu haben, der ausführt, sudo vimwenn der Dateieigentümer dies ist root.

Dies ist genauso einfach zu implementieren (es könnte sogar elegantere Implementierungen geben, ich bin eindeutig kein Bash-Guru):

function vim(){
  OWNER=$(stat -c '%U' $1)
  if [[ "$OWNER" == "root" ]]; then
    sudo /usr/bin/vim $*;
  else
    /usr/bin/vim $*;
  fi
}

Und es funktioniert wirklich gut.

Dies ist ein bashzentrierterer Ansatz als ein Ansatz, vimsodass es möglicherweise nicht jedem gefällt.

Na sicher:

  • Es gibt Anwendungsfälle, in denen dies fehlschlägt (wenn der Dateieigentümer dies nicht rooterfordert sudo, die Funktion jedoch trotzdem bearbeitet werden kann).
  • Es macht keinen Sinn, vimeine Datei nur zum Lesen zu verwenden (soweit es mich betrifft, verwende ich sie tailoder catfür kleine Dateien).

Aber ich finde, dass dies eine viel bessere Benutzererfahrung für Entwickler mit sich bringt , was meiner Meinung nach bei der Verwendung häufig vergessen wird bash. :-)

Augustin Riedinger
quelle
5
Sei dir nur bewusst, dass dies viel weniger verzeihend ist. Persönlich sind die meisten meiner Fehler dumme Fehler. Deshalb ziehe ich es vor, einen Kopf hoch zu bekommen, wenn ich etwas tue, das sowohl dumm als auch folgerichtig sein kann. Dies ist natürlich eine Frage der Präferenz, aber die Eskalation von Privilegien sollte eine gewissenhafte Handlung sein. Außerdem: Wenn Sie dies so oft erleben, dass ": w !!" genug Mühe, um lautlos sudo automatisch zu machen (aber nur, wenn owner = root); Möglicherweise möchten Sie Ihren aktuellen Workflow überprüfen.
Payne
Interessanter Kommentar, obwohl man sich dessen bewusst sein muss, denn wenn die Datei geöffnet wird, wird rootdas Passwort abgefragt.
Augustin Riedinger
Ah! Da ist der Unterschied. Es hängt davon ab, ob der Sudo-Benutzer "NOPASSWD" eingestellt hat oder nicht.
Payne
Dann mit NOPASSWD ist , was weniger nachsichtig ... :)
Augustin Riedinger
4

FÜR NEOVIM

Aufgrund von Problemen mit interaktiven Anrufen ( https://github.com/neovim/neovim/issues/1716 ) verwende ich dies für neovim, basierend auf der Antwort von Dr. Beco:

cnoremap w!! execute 'silent! write !SUDO_ASKPASS=`which ssh-askpass` sudo tee % >/dev/null' <bar> edit!

Dies öffnet einen Dialog, ssh-askpassin dem Sie nach dem sudo-Passwort gefragt werden.

dbranco
quelle