¿Cómo funciona el truco de vim "escribir con sudo"?

Muchos de ustedes probablemente hayan visto el comando que le permite escribir en un archivo que necesita permiso de root, incluso cuando olvidó abrir vim con sudo:

:w !sudo tee %

El caso es que no entiendo qué está sucediendo exactamente aquí.

Ya me he dado cuenta de esto: w es 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 |:!|.

por lo que pasa todas las líneas como entrada estándar.

La !sudo tee llamadas parciales tee con privilegios de administrador.

Para que todo tenga sentido, el % debe generar el nombre del archivo (como un parámetro para tee), pero no puedo encontrar referencias en la ayuda para este comportamiento.

tl; dr ¿Alguien podría ayudarme a analizar este comando?

preguntado el 08 de abril de 10 a las 11:04

@Nathan: Lo haría :w !sudo cat > % ¿No funciona tan bien y no contamina la salida estándar? -

@bjarkef - no, eso no funciona. En ese caso, sudo es aplicado a cat, pero no a >, por lo que no está permitido. Podría intentar ejecutar todo el comando en una subcapa sudo, como :w !sudo sh -c "cat % > yams.txt", pero eso tampoco funcionará, porque en el subshell, % es nulo borrará el contenido de su archivo. -

Solo deseo agregar que después de escribir ese comando, puede aparecer un mensaje de advertencia. Si es así, presione L. Luego, se le pedirá que presione enter. Hazlo y finalmente tendrás tu archivo guardado. -

@NathanLong @knittl: :w !sudo sh -c "cat >%" en realidad funciona tan bien como sudo tee % porque Vim sustituye el nombre de archivo por % antes de que llegue a la subcapa. Sin embargo, ninguno de ellos funciona si el nombre del archivo tiene espacios; tu tienes que hacer :w !sudo sh -c "cat >'%'" or :w !sudo tee "%" para arreglar eso. -

Guardar usando: W y recargar el archivo: comando W: ejecutar ': silencioso w! Sudo tee%> / dev / null' | :¡editar! -

9 Respuestas

In :w !sudo tee %...

% significa "el archivo actual"

As eugene y señaló, % de hecho significa "el nombre del archivo actual", que se pasa a tee para que sepa qué archivo sobrescribir.

(En los comandos de sustitución, es ligeramente diferente; como :help :% muestra, es equal to 1,$ (the entire file) (gracias a @Orafu por señalar que esto no se evalúa con el nombre del archivo). Por ejemplo, :%s/foo/bar significa "en el archivo actual, reemplazar apariciones de foo con bar. "Si resaltas algún texto antes de escribir :s, verá que las líneas resaltadas toman el lugar de % como su rango de sustitución).

:w no está actualizando su archivo

Una parte confusa de este truco es que podrías pensar :w está modificando su archivo, pero no lo está. Si abrió y modificó file1.txt, luego corrió :w file2.txt, sería un "guardar como"; file1.txt no se modificaría, pero el contenido actual del búfer se enviaría a file2.txt.

En lugar de file2.txt, puede sustituir un comando de shell para recibir el contenido del búfer. Por ejemplo, :w !cat solo mostrará el contenido.

Si Vim no se ejecutó con acceso sudo, es :w no se puede modificar un archivo protegido, pero si pasa el contenido del búfer al shell, un comando en el caparazón puede ser ejecutado con sudo. En este caso, usamos tee.

Entendiendo la camiseta

En cuanto a los tee, imagina el tee comando como una tubería en forma de T en una situación normal de tubería bash: dirige la salida a archivos especificados y también lo envía a salida estándar, que puede ser capturado por 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 pasado a grep.

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

(Diagrama creado con flujo ascii.)

Consulte las tee página man para más información.

Tee como un truco

En la situación que describe su pregunta, usar tee es un truco porque ignoramos la mitad de lo que hace. sudo tee escribe 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 estamos usando tee como una forma alternativa de escribir un archivo y para que podamos llamarlo con sudo.

Haciendo este truco fácil

Puede agregar esto a su .vimrc para que este truco sea fácil de usar: solo escribe :w!!.

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

La > /dev/null parte explícitamente desecha la salida estándar, ya que, como dije, no necesitamos pasar nada a otro comando canalizado.

Respondido 07 Oct 19, 16:10

Especialmente como tu notación "w !!" que es tan fácil de recordar después de usar "sudo !!" en la línea de comando. - Aidan Kane

Entonces esto usa tee por su capacidad para escribir stdin en un archivo. Me sorprende que no haya un programa cuyo trabajo sea hacer eso (encontré un programa del que nunca había oído hablar llamado sponge que hace esto). Supongo que el típico "escribir una secuencia en un archivo" se realiza mediante un shell integrado. ¿Vim's !{cmd} no bifurcar una concha (bifurcar cmd en lugar de)? Quizás algo más obvio sería utilizar alguna variante funcional de sh -c ">" más bien que tee. - steven lu

@Steven Lu: sponge es parte del moreutils paquete en casi todas las distribuciones excepto distribuciones basadas en Debian. moreutils tiene algunas herramientas bastante buenas que están a la par con herramientas más comunes como xargs y tee. - suizo

¿Cómo expandir este alias para decirle también a vim que cargue automáticamente el contenido del archivo modificado en el búfer actual? Me lo pide, ¿cómo automatizarlo? - Zlatko

@ user247077: En ese caso, cat se ejecuta como root y la salida es redirigida por el shell, que no se ejecuta como root. Es lo mismo que echo hi > /read/only/file. - por que no hugo

En la línea de comando ejecutada, % representa el nombre de archivo actual. Esto está documentado en :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.

Como ya has descubierto :w !cmd canaliza el contenido del búfer actual a otro comando. Qué tee lo que hace 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/null escribe efectivamente el contenido del búfer actual en el archivo actual siendo root. Otro comando que se puede usar para esto es dd:

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

Como atajo, puede agregar este mapeo a su .vimrc:

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

Con lo anterior puedes escribir :w!!<Enter> para guardar el archivo como root.

Respondido el 25 de junio de 17 a las 00:06

Interesante, :help _% trae a colación lo que ingresaste, pero :help % muestra la clave de coincidencia de llaves. No habría pensado en probar el prefijo de subrayado, ¿es un patrón de algún tipo en la documentación de vim? ¿Hay otras cosas "especiales" que pueda probar cuando busque ayuda? - david papa

@David: El help comando salta a una etiqueta. Puede ver las etiquetas disponibles con :h help-tags. También puede utilizar la finalización de la línea de comandos para ver las etiquetas coincidentes: :h cmdline<Ctrl-D> (o :h cmdline<Tab> si te pones wildmode respectivamente) - Eugene Yarmash

Tuve que usar cmap w!! w !sudo tee % > /dev/null en mi archivo .vimrc para que esto funcione. Es el % perdido en la respuesta anterior? (No hay ningún experto en vim aquí). dmmfll

@DMfll Sí, lo es. El comando en la respuesta resultaría en sudo tee > /dev/null /path/to/current/file que realmente no tiene sentido. (Voy a editar eso) - jazz pi

@jazzpi: Estás equivocado. A los shells en realidad no les importa en qué parte de la línea de comandos se realiza la redirección de archivos. - Eugene Yarmash

:w - Escribe un archivo.

!sudo - Llamar al comando shell sudo.

tee - La salida del comando write (vim: w) redirigida 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 la entrada estándar y la escribe en un archivo representado por%. Sin embargo, esto le pedirá que vuelva a cargar el archivo (presione L para cargar los cambios en vim):

enlace tutorial

Respondido el 04 de junio de 11 a las 07:06

Esto también funciona bien:

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

Esto está inspirado en el comentario de @Nathan Long.

AVISO:

" debe usarse en lugar de ' porque queremos % para expandirse antes de pasar a shell.

Respondido 29 Jul 14, 09:07

Si bien esto puede funcionar, también le da a sudo acceso a múltiples programas (sh y cat). Los otros ejemplos podrían ser más seguros reemplazando tee con /usr/bin/tee para prevenir ataques de modificación de PATH. - idbrii

La respuesta aceptada lo cubre todo, así que solo daré otro ejemplo de un atajo que yo uso, para que conste.

Agrégalo a tu etc/vim/vimrc (o ~/.vimrc):

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

Dónde:

  • cnoremap: dice empuje que el siguiente atajo se asociará en la línea de comando.
  • w!!: el atajo en sí.
  • execute '...': un comando que ejecuta la siguiente cadena.
  • silent!: ejecutarlo en silencio
  • write !sudo tee % >/dev/null: la pregunta OP, agregó una redirección de mensajes a NULL para hacer un comando limpio
  • <bar> edit!: este truco es la cereza del pastel: también llama al edit comando para recargar el búfer y luego evitar mensajes como el búfer ha cambiado. <bar> es como escribir el tubo símbolo para separar dos comandos aquí.

Espero eso ayude. Consulte también otros problemas:

Respondido el 13 de enero de 18 a las 07:01

¡silencio! deshabilitó la solicitud de contraseña para que no la vea - andy rayo

Me gustaría sugerir otro enfoque a la "Oups me olvidé de escribir sudo al abrir mi archivo " problema:

En lugar de recibir un permission deniedy tener que escribir :w!!, Me parece más elegante tener un condicional vim comando que hace sudo vim si el propietario del archivo es root.

Esto es tan fácil de implementar (incluso podría haber implementaciones más elegantes, claramente no soy un bash-guru):

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

Y funciona muy bien.

Este es un mas bashenfoque centrado que un vim-uno para que no le guste a todo el mundo.

Por supuesto:

  • hay casos de uso en los que fallará (cuando el propietario del archivo no es root pero requiere sudo, pero la función se puede editar de todos modos)
  • no tiene sentido cuando se usa vim para leer solo un archivo (en lo que a mí respecta, utilizo tail or cat para archivos pequeños)

Pero encuentro que esto trae una mucho mejor experiencia de usuario dev, que es algo que en mi humilde opinión tiende a olvidarse cuando se usa bash. :-)

Respondido 28 Feb 18, 18:02

Solo tenga en cuenta que esto es mucho menos indulgente. Personalmente, la mayoría de mis errores son errores estúpidos. Así que prefiero avisarme cuando estoy haciendo algo que puede ser estúpido y consecuente. Por supuesto, esto es una cuestión de preferencia, pero la escalada de privilegios debe ser un acto de conciencia. Además: si experimenta esto con tanta frecuencia como para hacer ": w !!" suficiente molestia para auto sudo silenciosamente (pero solo si propietario = root); es posible que desee examinar su flujo de trabajo actual. - Payne

Comentario interesante, aunque uno debe ser consciente porque cuando el archivo se abre como root se consulta la contraseña. - Agustín Riedinger

¡Ah! Esa es la diferencia. Depende de si el usuario de sudo tiene configurado "NOPASSWD" o no. - Payne

Entonces tener NOPASSWD is lo que es menos indulgente ... :) - Agustín Riedinger

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:

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

Esto abrirá un diálogo usando ssh-askpass pidiendo la contraseña de sudo.

Respondido 03 Oct 19, 10:10

Un resumen (y una mejora muy pequeña) de las respuestas más comunes que encontré para esto en 2020.

tl; dr

Llamar con :w!! or :W!!. Después de que se expanda, presione enter.

  • Si es demasiado lento para escribir el !! después del w / W, no se expandirá y podría informar: E492: Not an editor command: W!!

NOTA Utiliza which tee salida para reemplazar /usr/bin/tee si difiere en tu caso.

Pon esto en tu ~/.vimrc archivo:

    " Silent version of the super user edit, sudo tee trick.
    cnoremap W!! execute 'silent! write !sudo /usr/bin/tee "%" >/dev/null' <bar> edit!
    " Talkative version of the super user edit, sudo tee trick.
    cmap w!! w !sudo /usr/bin/tee >/dev/null "%"

Más información:

Primero, la respuesta vinculada a continuación fue la única otra que pareció mitigar la mayoría de los problemas conocidos y diferir de manera significativa de las demás. Vale la pena leer: https://stackoverflow.com/a/12870763/2927555

Mi respuesta anterior se reunió a partir de múltiples sugerencias sobre el tema de la camiseta sudo convencional y, por lo tanto, mejora ligeramente las respuestas más comunes que encontré. Mi versión anterior:

  • Funciona con espacios en blanco en los nombres de los archivos

  • Mitiga los ataques de modificación de ruta especificando la ruta completa al tee.

  • Te da dos asignaciones, W !! para la ejecución silenciosa, y w !! para no silencioso, es decir, hablador :-)

  • La diferencia en el uso de la versión no silenciosa es que puede elegir entre [O] k y [L] oad. Si no le importa, use la versión silenciosa.

    • [OK - Conserva su historial de deshacer, pero hará que reciba una advertencia cuando intente salir. Tienes que usar: q! abandonar.
    • [Carga - Borra su historial de deshacer y restablece la "bandera modificada", lo que le permite salir sin recibir una advertencia para guardar los cambios.

La información para lo anterior se extrajo de un montón de otras respuestas y comentarios sobre esto, pero en particular:

Respuesta del Dr. Beco: https://stackoverflow.com/a/48237738/2927555

comentario de idbrii a esto: https://stackoverflow.com/a/25010815/2927555

Comentario de Han Seoul-Oh a esto: ¿Cómo funciona el truco de vim "escribir con sudo"?

Bruno Bronosky comenta a esto: https://serverfault.com/a/22576/195239

Esta respuesta también explica por qué el enfoque aparentemente más simple no es tan buena idea: https://serverfault.com/a/26334/195239

Respondido 15 ago 20, 08:08

El único problema con cnoremap w!! es que reemplaza w con ! (y se cuelga hasta que escribe el siguiente carácter) cada vez que escribe w! en el : símbolo del sistema. Como cuando realmente quieres forzar el guardado con w!. Además, incluso si no es lo primero después :.

Por lo tanto, sugeriría mapearlo a algo como <Fn>w. Yo personalmente tengo mapleader = F1, así que estoy usando <Leader>w.

Respondido 10 Feb 21, 20:02

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.