Beaucoup d'entre vous ont probablement vu la commande qui vous permet d'écrire sur un fichier nécessitant une autorisation root, même si vous avez oublié d'ouvrir vim avec sudo:
:w !sudo tee %
Le fait est que je ne comprends pas exactement ce qui se passe ici.
Je l'ai déjà compris:
w
c'est pour ça
*: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 |:!|.
il passe donc toutes les lignes en entrée standard.
La !sudo tee
partie appelle tee
avec des privilèges d'administrateur.
Pour que tout ait un sens, le %
devrait afficher le nom de fichier (en tant que paramètre pour tee
), mais je ne trouve pas de références dans l'aide pour ce comportement.
tl; dr Quelqu'un pourrait-il m'aider à disséquer cette commande?
:w !sudo cat > %
fonctionnerait pas aussi bien et ne polluerait pas la sortie standard?sudo
est appliqué àcat
, mais pas à>
, il n'est donc pas autorisé. Vous pouvez essayer d'exécuter la commande entière dans un sous-shell sudo, comme:w !sudo sh -c "cat % > yams.txt"
, mais cela ne fonctionnera pas non plus, car dans le sous-shell,%
est nul; vous supprimerez le contenu de votre fichier.:w !sudo sh -c "cat >%"
fonctionne en fait aussi bien quesudo tee %
parce que Vim remplace le nom de fichier%
avant qu'il n'atteigne le sous-shell. Cependant, aucun d'eux ne fonctionne si le nom de fichier contient des espaces; vous devez faire:w !sudo sh -c "cat >'%'"
ou:w !sudo tee "%"
résoudre ce problème.Réponses:
Dans
:w !sudo tee %
...%
signifie "le fichier courant"Comme l'a souligné eugene y ,
%
cela signifie en effet "le nom du fichier actuel", qui est transmis pourtee
qu'il sache à quel fichier écraser.(Dans les commandes de substitution, c'est légèrement différent; comme le
:help :%
montre, c'estequal to 1,$ (the entire file)
(merci à @Orafu d'avoir souligné que cela n'évalue pas le nom du fichier). Par exemple,:%s/foo/bar
signifie " dans le fichier actuel , remplacez les occurrences defoo
parbar
". Si vous mettez en surbrillance du texte avant de taper:s
, vous verrez que les lignes en surbrillance%
remplacent votre plage de substitution.):w
ne met pas à jour votre fichierUne partie déroutante de cette astuce est que vous pensez peut-être
:w
modifier votre fichier, mais ce n'est pas le cas. Si vous avez ouvert et modifiéfile1.txt
, puis exécuté:w file2.txt
, ce serait un "enregistrer sous";file1.txt
ne serait pas modifié, mais le contenu du tampon actuel serait envoyé àfile2.txt
.Au lieu de
file2.txt
, vous pouvez remplacer une commande shell pour recevoir le contenu du tampon . Par exemple,:w !cat
affichera simplement le contenu.Si Vim n'a pas été exécuté avec l'accès sudo,
:w
il ne peut pas modifier un fichier protégé, mais s'il transmet le contenu du tampon au shell, une commande dans le shell peut être exécutée avec sudo . Dans ce cas, nous utilisonstee
.Comprendre le tee
Quant à
tee
, imaginez latee
commande comme un tuyau en forme de T dans une situation de tuyauterie bash normale: elle dirige la sortie vers le (s) fichier (s) spécifié (s) et l' envoie également vers la sortie standard , qui peut être capturée par la prochaine commande canalisée.Par exemple, dans
ps -ax | tee processes.txt | grep 'foo'
, la liste des processus sera écrite dans un fichier texte et transmise àgrep
.(Diagramme créé avec Asciiflow .)
Voir la
tee
page de manuel pour plus d'informations.Tee comme un hack
Dans la situation décrite dans votre question, l' utilisation
tee
est un hack car nous ignorons la moitié de ce qu'elle fait .sudo tee
écrit dans notre fichier et envoie également le contenu du tampon à la sortie standard, mais nous ignorons la sortie standard . Nous n'avons pas besoin de passer quoi que ce soit à une autre commande canalisée dans ce cas; nous utilisons justetee
comme une autre façon d'écrire un fichier et pour que nous puissions l'appeler avecsudo
.Rendre cette astuce facile
Vous pouvez l'ajouter à votre
.vimrc
pour rendre cette astuce facile à utiliser: il suffit de taper:w!!
.La
> /dev/null
partie jette explicitement la sortie standard, car, comme je l'ai dit, nous n'avons pas besoin de passer quoi que ce soit à une autre commande canalisée.la source
tee
pour sa capacité à écrire stdin dans un fichier. Je suis surpris qu'il n'y ait pas de programme dont le travail consiste à faire cela (j'ai trouvé un programme dont je n'ai jamais entendu parler appelésponge
qui fait cela). Je suppose que le type "écrire un flux dans un fichier" est effectué par un shell intégré. Est-ce que Vim!{cmd}
ne bifurque pas une coquille (en forçant à lacmd
place)? Peut-être que quelque chose de plus évident serait d'utiliser une variante de travail desh -c ">"
plutôt que detee
.sponge
fait partie dumoreutils
paquet sur à peu près toutes les distributions sauf les distributions basées sur Debian.moreutils
a de très bons outils qui sont à égalité avec des outils plus courants commexargs
ettee
.cat
s'exécute en tant que root et la sortie est redirigée par le shell, qui ne s'exécute pas en tant que root. C'est la même chose queecho hi > /read/only/file
.Dans la ligne de commande exécutée,
%
représente le nom de fichier actuel . Ceci est documenté dans:help cmdline-special
:Comme vous l'avez déjà découvert,
:w !cmd
redirige le contenu du tampon actuel vers une autre commande. Qu'esttee
- ce que c'est copier l'entrée standard dans un ou plusieurs fichiers, et aussi sur la sortie standard. Par conséquent,:w !sudo tee % > /dev/null
écrit efficacement le contenu du tampon actuel dans le fichier actuel tout en étant root . Une autre commande qui peut être utilisée pour cela estdd
:En tant que raccourci, vous pouvez ajouter ce mappage à votre
.vimrc
:Avec ce qui précède, vous pouvez taper
:w!!<Enter>
pour enregistrer le fichier en tant que root.la source
:help _%
affiche ce que vous avez entré, mais:help %
affiche la clé de correspondance d'accolade. Je n'aurais pas pensé essayer le préfixe de soulignement, est-ce un modèle quelconque dans la documentation de vim? Y a-t-il d'autres choses «spéciales» à essayer lorsque vous cherchez de l'aide?help
commande passe à une balise. Vous pouvez voir les balises disponibles avec:h help-tags
. Vous pouvez également utiliser l'achèvement de la ligne de commande pour voir les balises correspondantes::h cmdline<Ctrl-D>
(ou:h cmdline<Tab>
si vous définissez enwildmode
conséquence)cmap w!! w !sudo tee % > /dev/null
dans mon fichier .vimrc pour que cela fonctionne. Est-ce%
mal placé dans la réponse ci-dessus? (Aucun expert vim ici.)sudo tee > /dev/null /path/to/current/file
qui n'a pas vraiment de sens. (Je vais le modifier)Cela fonctionne également bien:
Ceci est inspiré par le commentaire de @Nathan Long.
AVIS :
"
doit être utilisé à la place de'
car nous voulons%
être développés avant de passer au shell.la source
tee
par/usr/bin/tee
pour empêcher les attaques de modification de PATH.:w
- Écrivez un fichier.!sudo
- Appelez la commande shell sudo.tee
- La sortie de la commande write (vim: w) redirigée à l'aide de tee. Le% n'est rien d'autre que le nom de fichier actuel, c'est-à-dire /etc/apache2/conf.d/mediawiki.conf. En d'autres termes, la commande tee est exécutée en tant que root et prend une entrée standard et l'écrit dans un fichier représenté par%. Cependant, cela vous invitera à recharger à nouveau le fichier (appuyez sur L pour charger les modifications dans vim lui-même):lien du tutoriel
la source
La réponse acceptée couvre tout, donc je vais donner un autre exemple de raccourci que j'utilise, pour mémoire.
Ajoutez-le à votre
etc/vim/vimrc
(ou~/.vimrc
):cnoremap w!! execute 'silent! write !sudo tee % >/dev/null' <bar> edit!
Où:
cnoremap
: indique à vim que le raccourci suivant doit être associé dans la ligne de commande.w!!
: le raccourci lui-même.execute '...'
: une commande qui exécute la chaîne suivante.silent!
: exécutez-le en silencewrite !sudo tee % >/dev/null
: la question OP, ajout d'une redirection de messagesNULL
pour faire une commande propre<bar> edit!
: cette astuce est la cerise sur le gâteau: elle appelle aussi laedit
commande pour recharger le buffer puis éviter les messages tels que le buffer a changé .<bar>
est de savoir comment écrire le symbole de tuyau pour séparer deux commandes ici.J'espère que cela aide. Voir aussi pour d'autres problèmes:
la source
Je voudrais suggérer une autre approche au problème "Oups que j'ai oublié d'écrire
sudo
lors de l'ouverture de mon fichier" :Au lieu de recevoir un
permission denied
, et d'avoir à taper:w!!
, je trouve plus élégant d'avoir unevim
commande conditionnelle qui faitsudo vim
si le propriétaire du fichier l'estroot
.C'est aussi facile à implémenter (il pourrait même y avoir des implémentations plus élégantes, je ne suis clairement pas un bash-gourou):
Et ça marche vraiment bien.
Il s'agit d'une
bash
approche plus centrée qu'unevim
- donc tout le monde ne l'aimera peut-être pas.Bien sûr:
root
mais l'exigesudo
, mais que la fonction peut être modifiée de toute façon)vim
en lecture seule d'un fichier (en ce qui me concerne, j'utilisetail
oucat
pour de petits fichiers)Mais je trouve que cela apporte une bien meilleure expérience utilisateur de développement , ce qui est quelque chose que IMHO a tendance à oublier lors de l'utilisation
bash
. :-)la source
root
le mot de passe est demandé.POUR NEOVIM
En raison de problèmes avec les appels interactifs ( https://github.com/neovim/neovim/issues/1716 ), je l'utilise pour neovim, sur la base de la réponse du Dr Beco:
Cela ouvrira une boîte de dialogue utilisant la
ssh-askpass
demande du mot de passe sudo.la source