Muchos de ustedes probablemente hayan visto el comando que les permite escribir en un archivo que necesita permiso de root, incluso cuando olvidaron abrir vim con sudo:
:w !sudo tee %
Lo que pasa es que no entiendo exactamente lo que está sucediendo aquí.
Ya he imaginado esto:
wes para esto
*: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 |:!|.
entonces pasa todas las líneas como entrada estándar.
La !sudo teeparte llama teecon privilegios de administrador.
Para que todo tenga sentido, %deberían mostrar el nombre de archivo (como parámetro para tee), pero no puedo encontrar referencias sobre la ayuda para este comportamiento.
tl; dr ¿Podría alguien ayudarme a diseccionar este comando?

:w !sudo cat > %No funcionaría tan bien y no contaminaría la producción estándar?sudose aplica acat, pero no a>, por lo que no está permitido. Puede intentar ejecutar todo el comando en una subshell sudo, como:w !sudo sh -c "cat % > yams.txt", pero eso tampoco funcionará, porque en la subshell,%es nulo; Vaciará el contenido de su archivo.:w !sudo sh -c "cat >%"realidad funciona tan bien comosudo tee %porque Vim sustituye el nombre del archivo%antes de que llegue a la subshell. Sin embargo, ninguno de ellos funciona si el nombre de archivo tiene espacios; tienes que hacer:w !sudo sh -c "cat >'%'"o:w !sudo tee "%"arreglar eso.Respuestas:
En
:w !sudo tee %...%significa "el archivo actual"Como eugene y señaló , de
%hecho significa "el nombre del archivo actual", que se pasa parateeque sepa qué archivo sobrescribir.(En los comandos de sustitución, es ligeramente diferente; como se
:help :%muestra, esequal to 1,$ (the entire file)(gracias a @Orafu por señalar que esto no evalúa el nombre del archivo). Por ejemplo,:%s/foo/barsignifica " en el archivo actual , reemplace las ocurrencias defooconbar". algo de texto antes de escribir:s, verá que las líneas resaltadas toman el lugar%como su rango de sustitución).:wno está actualizando su archivoUna parte confusa de este truco es que podría pensar que
:westá modificando su archivo, pero no lo es. Si abrió y modificófile1.txt, luego ejecutó:w file2.txt, sería un "guardar como";file1.txtno se modificaría, pero el contenido actual del búfer se enviaría afile2.txt.En lugar de
file2.txt, puede sustituir un comando de shell para recibir el contenido del búfer . Por ejemplo,:w !catsolo mostrará los contenidos.Si Vim no se ejecutó con acceso sudo,
:wno puede modificar un archivo protegido, pero si pasa el contenido del búfer al shell, se puede ejecutar un comando en el shell con sudo . En este caso, usamostee.Camiseta de comprensión
En cuanto a
tee, imagine elteecomando como una tubería en forma de T en una situación de tubería de bash normal: dirige la salida a los archivos especificados y también lo envía a la salida estándar , que puede capturarse con el siguiente comando canalizado.Por ejemplo, en
ps -ax | tee processes.txt | grep 'foo', la lista de procesos se escribirá en un archivo de texto y se pasará agrep.(Diagrama creado con Asciiflow ).
Vea la
teepágina del manual para más información.Tee como un truco
En la situación que describe su pregunta, usar
teees un hack porque estamos ignorando la mitad de lo que hace .sudo teeescribe en nuestro archivo y también envía el contenido del búfer a la salida estándar, pero ignoramos la salida estándar . No necesitamos pasar nada a otro comando canalizado en este caso; solo lo estamos utilizandoteecomo una forma alternativa de escribir un archivo y para poder llamarlosudo.Hacer este truco fácil
Puedes agregar esto a tu
.vimrcpara hacer que este truco sea fácil de usar: solo escribe:w!!.La
> /dev/nullparte arroja explícitamente la salida estándar, ya que, como dije, no necesitamos pasar nada a otro comando canalizado.fuente
teepor su capacidad de escribir stdin en un archivo. Me sorprende que no haya un programa cuyo trabajo sea hacer eso (encontré un programa del que nunca escuché llamarspongeque hace esto). Supongo que el típico "escribir una secuencia en un archivo" es realizado por un shell incorporado. ¿Vim!{cmd}no bifurca un caparazón (encmdcambio, se bifurca )? Quizás algo que sea más obvio sería usar alguna variante de trabajo ensh -c ">"lugar detee.spongees parte delmoreutilspaquete en casi todas las distribuciones, excepto en las distribuciones basadas en Debian.moreutilstiene algunas herramientas bastante buenas que están a la par con herramientas más comunes comoxargsytee.catejecuta como root y el shell redirige la salida, que no se ejecuta como root. Es lo mismo queecho hi > /read/only/file.En la línea de comando ejecutada,
%representa el nombre del archivo actual . Esto está documentado en:help cmdline-special:Como ya descubrió,
:w !cmdcanaliza el contenido del búfer actual a otro comando. Lo queteehace es copiar la entrada estándar a uno o más archivos, y también a la salida estándar. Por lo tanto,:w !sudo tee % > /dev/nullescribe efectivamente el contenido del búfer actual en el archivo actual mientras es root . Otro comando que se puede usar para esto esdd:Como acceso directo, puede agregar esta asignación a su
.vimrc:Con lo anterior, puede escribir
:w!!<Enter>para guardar el archivo como root.fuente
:help _%muestra lo que ingresó, pero:help %muestra la clave de coincidencia de llaves. No hubiera pensado probar el prefijo de subrayado, ¿es ese un patrón de algún tipo en la documentación de vim? ¿Hay otras cosas 'especiales' para probar al buscar ayuda?helpcomando salta a una etiqueta. Puede ver las etiquetas disponibles con:h help-tags. También puede usar la finalización de la línea de comandos para ver las etiquetas coincidentes::h cmdline<Ctrl-D>(o:h cmdline<Tab>si lo configura enwildmodeconsecuencia)cmap w!! w !sudo tee % > /dev/nullen mi archivo .vimrc para que esto funcionara. ¿Está%mal ubicado en la respuesta anterior? (No hay ningún experto vim aquí.)sudo tee > /dev/null /path/to/current/filelo que realmente no tiene sentido. (Voy a editar eso)Esto también funciona bien:
Esto está inspirado en el comentario de @Nathan Long.
AVISO :
"debe usarse en lugar de'porque queremos%expandirnos antes de pasar a shell.fuente
teecon/usr/bin/teepara prevenir ataques PATH-modificación.:w- Escribe un archivo.!sudo- Llamar al comando shell sudo.tee- La salida del comando write (vim: w) redirigido usando tee. El% no es más que el nombre del archivo actual, es decir, /etc/apache2/conf.d/mediawiki.conf. En otras palabras, el comando tee se ejecuta como root y toma una entrada estándar y lo escribe en un archivo representado por%. Sin embargo, esto le indicará que vuelva a cargar el archivo nuevamente (presione L para cargar los cambios en vim):enlace tutorial
fuente
La respuesta aceptada lo cubre todo, así que solo daré otro ejemplo de un atajo que uso, para el registro.
Agréguelo a su
etc/vim/vimrc(o~/.vimrc):cnoremap w!! execute 'silent! write !sudo tee % >/dev/null' <bar> edit!Dónde:
cnoremap: le dice a vim que el siguiente acceso directo se asociará en la línea de comando.w!!: el atajo en sí.execute '...': un comando que ejecuta la siguiente cadena.silent!: ejecutarlo en silenciowrite !sudo tee % >/dev/null: la pregunta OP, agregó una redirección de mensajesNULLpara hacer un comando limpio<bar> edit!: este truco es la guinda del pastel: también llama aleditcomando para volver a cargar el búfer y luego evitar mensajes como que el búfer ha cambiado .<bar>es cómo escribir el símbolo de tubería para separar dos comandos aquí.Espero eso ayude. Ver también para otros problemas:
fuente
Me gustaría sugerir otro enfoque para el problema "Oups que olvidé escribir
sudoal abrir mi archivo" :En lugar de recibir un
permission denied, y tener que escribir:w!!, me parece más elegante tener unvimcomando condicional que lo hacesudo vimsi el propietario del archivo esroot.Esto es tan fácil de implementar (incluso podría haber implementaciones más elegantes, claramente no soy un bash-guru):
Y funciona muy bien.
Este es un
bashenfoque más centrado quevimuno, por lo que no a todos les gustaría.Por supuesto:
rootpero lo requieresudo, pero la función se puede editar de todos modos)vimpara leer solo un archivo (en lo que a mí respecta, usotailocatpara archivos pequeños)Pero creo que esto brinda una experiencia de usuario de desarrollo mucho mejor , que es algo que en mi humilde opinión tiende a olvidarse cuando se usa
bash. :-)fuente
rootse consulta la contraseña.Para Neovim
Debido a problemas con las llamadas interactivas ( https://github.com/neovim/neovim/issues/1716 ), estoy usando esto para neovim, según la respuesta del Dr. Beco:
Esto abrirá un cuadro de diálogo con
ssh-askpassla solicitud de la contraseña de sudo.fuente