it-swarm.com.de

Wie funktioniert der Trick "Mit Sudo schreiben"?

Viele von Ihnen haben wahrscheinlich den Befehl gesehen, mit dem Sie in eine Datei schreiben können, die Root-Rechte benötigt, auch wenn Sie vergessen haben, vim mit Sudo zu öffnen:

:w !Sudo tee %

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

Das habe ich mir schon gedacht: w ist 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 Teil !Sudo tee ruft tee mit Administratorrechten auf.

Damit alles Sinn macht, sollte der % den Dateinamen ausgeben (als Parameter für tee), aber ich kann keine Referenzen in der Hilfe für dieses Verhalten finden.

tl; dr Kann mir jemand helfen, diesen Befehl zu zerlegen?

1306
Doppelganger

In :w !Sudo tee %...

% bedeutet "die aktuelle Datei"

Wie eugene y darauf hingewiesen , % bedeutet in der Tat "der aktuelle Dateiname". Eine andere Verwendung hierfür in Vim sind Substitutionsbefehle. Zum Beispiel bedeutet :%s/foo/bar "in der aktuellen Datei Ersetzen Sie das Vorkommen von foo durch bar". Wenn Sie Text markieren, bevor Sie :s eingeben, werden Sie feststellen, dass die markierten Linien den Platz von % als Ihren Ersetzungsbereich einnehmen.

:w aktualisiert Ihre Datei nicht

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

Anstelle von file2.txt können Sie einen Shell-Befehl einsetzen, um den Pufferinhalt zu erhalten. Zum Beispiel zeigt :w !cat nur den Inhalt an.

Wenn Vim nicht mit Sudo-Zugriff ausgeführt wurde, kann sein :w eine geschützte Datei nicht ändern, aber wenn er den Pufferinhalt an die Shell übergibt, ein Befehl in der Shell kann mit Sudo ausgeführt werden. In diesem Fall verwenden wir tee.

Tee verstehen

Stellen Sie sich für tee den Befehl tee als T-förmiges Rohr in einer normalen Bash-Piping-Situation vor: Er leitet die Ausgabe an die angegebenen Dateien und sendet sie auch an die Standardausgabe, die von erfasst werden kann der nächste Pipe-Befehl.

Beispielsweise wird in ps -ax | tee processes.txt | grep 'foo' die Liste der Prozesse in eine Textdatei geschrieben nd, die an grep übergeben wird.

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

(Diagramm erstellt mit Asciiflow .)

Weitere Informationen finden Sie in der tee man page .

Tee als Hack

In der Situation, die Ihre Frage beschreibt, ist mit tee ein Hack, weil wir die Hälfte dessen ignorieren, was es tut. Sudo tee schreibt 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 Pipe-Befehl übergeben. Wir verwenden nur tee als alternative Methode zum Schreiben einer Datei, damit wir sie mit Sudo aufrufen können.

Machen Sie diesen Trick einfach

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

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

Der > /dev/null -Teil explizit ​​verwirft die Standardausgabe, da wir, wie gesagt, nichts an einen anderen Pipe-Befehl übergeben müssen.

1485
Nathan Long

In der ausgeführten Befehlszeile steht _%_ 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, leitet _:w !cmd_ den Inhalt des aktuellen Puffers an einen anderen Befehl weiter. Was tee bewirkt, ist das Kopieren der Standardeingabe in eine oder mehrere Dateien und auch in die Standardausgabe. Daher schreibt _:w !Sudo tee % > /dev/null_ effektiv den Inhalt des aktuellen Puffers in die aktuelle Datei , während er root ist . Ein weiterer Befehl, der hierfür verwendet werden kann, ist dd :

_:w !Sudo dd of=% > /dev/null
_

Als Abkürzung können Sie diese Zuordnung zu Ihrem _.vimrc_ hinzufügen:

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

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

93
Eugene Yarmash

Das funktioniert auch gut:

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

Dies wird durch den Kommentar von @ Nathan Long inspiriert.

NOTICE:

" muss anstelle von ' verwendet werden, da % vor der Übergabe an Shell erweitert werden soll.

18
feihu

:w - Schreiben Sie eine Datei.

!Sudo - Ruft den Shell Sudo-Befehl auf.

tee - Die Ausgabe des Befehls write (vim: w), der mit tee umgeleitet wird. Das% ist nichts anderes als der aktuelle Dateiname, d. H. /Etc/Apache2/conf.d/mediawiki.conf. Mit anderen Worten, der Befehl tee wird als root ausgeführt und verwendet die Standardeingabe und schreibt ihn in eine Datei, die durch% dargestellt wird. Dies fordert Sie jedoch auf, die Datei erneut zu laden (drücken Sie L, um die Änderungen in vim selbst zu laden):

Tutorial-Link

16
kev

Die akzeptierte Antwort deckt alles ab, also 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: teilt vim mit, dass die folgende Verknüpfung in der Befehlszeile verknüpft werden soll.
  • w!!: die Verknüpfung selbst.
  • execute '...': Ein Befehl, der die folgende Zeichenfolge ausführt.
  • silent!: Lass es ruhig laufen
  • write !Sudo tee % >/dev/null: Bei der OP-Frage wurde eine Umleitung von Nachrichten zu NULL hinzugefügt, um einen sauberen Befehl auszuführen
  • <bar> edit!: Dieser Trick ist die Kirsche des Kuchens: Er ruft auch den Befehl edit auf, um den Puffer neu zu laden und dann Meldungen wie zu vermeiden, die der Puffer geändert hat . <bar> beschreibt, wie das Pipe-Symbol geschrieben wird, um hier zwei Befehle zu trennen.

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

6
Dr Beco

Ich möchte einen anderen Ansatz für das Problem "Oups, ich habe vergessen, beim Öffnen meiner Datei Sudo zu schreiben" vorschlagen:

Anstatt einen permission denied zu erhalten und :w!! eingeben zu müssen, finde ich es eleganter, einen bedingten Befehl vim zu haben, der Sudo vim ausführt, wenn der Dateieigentümer root ist.

Dies ist ebenso 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 mehr bash- zentrierter Ansatz als ein vim-, sodass es möglicherweise nicht jedem gefällt.

Na sicher:

  • es gibt Anwendungsfälle, in denen dies fehlschlägt (wenn der Dateieigentümer nicht root ist, aber Sudo erfordert, die Funktion jedoch trotzdem bearbeitet werden kann).
  • es ist nicht sinnvoll, wenn vim nur zum Lesen einer Datei verwendet wird (ich verwende tail oder cat für kleine Dateien).

Aber ich finde, das bringt eine viel bessere dev User Experience, was meiner Meinung nach bei der Verwendung von bash eher vergessen wird. :-)

4