Pregunta ¿Cómo funciona el truco vim "write with sudo"?


Muchos de ustedes probablemente hayan visto el comando que les permite escribir en un archivo que necesita permiso de raíz, incluso cuando olvidaron abrir vim con sudo:

:w !sudo tee %

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

Ya he pensado esto: w es por 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 tanto, pasa todas las líneas como entrada estándar.

los !sudo tee llamadas de parte tee con privilegios de administrador.

Para que todo tenga sentido, el % debería dar salida al nombre del archivo (como un 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?


1148
2018-04-08 14:36


origen


Respuestas:


% significa "el archivo actual"

Como Eugene y señaló, % de hecho significa "el nombre del archivo actual". Otro uso para esto en Vim es en los comandos de sustitución. Por ejemplo, :%s/foo/bar significa "en el archivo actual, reemplaza las ocurrencias de foo con bar. "Si resaltas un 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 es así. 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, usted puede sustituir un comando de shell para recibir el contenido del buffer. Por ejemplo, :w !cat solo mostrará los contenidos.

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

Comprender el tee

Como para tee, imagina el tee comando como una tubería en forma de T en una situación de tubería bash normal: dirige la salida a los 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 pasó a grep.

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

(Diagrama creado con Asciiflow.)

Ver el tee página man para más información.

Tee como un truco

En la situación que describe su pregunta, utilizando 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 buffer a la salida estándar, pero ignoramos la producción 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 sudo.

Haciendo fácil este truco

Puedes agregar esto a tu .vimrc para hacer este truco fácil de usar: simplemente escribe :w!!.

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

los > /dev/nullparte explícitamente descarta la salida estándar, ya que, como dije, no necesitamos pasar nada a otro comando entubado.


1272
2017-08-16 12:49



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 does 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 mientras eres root. Otro comando que se puede usar para esto es dd:

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

Como atajo, puede agregar esta asignación a su .vimrc:

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

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


88
2018-04-08 14:45



Esto también funciona bien:

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

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

DARSE CUENTA:

" debe ser utilizado en lugar de ' porque queremos % para expandir antes de pasar al caparazón.


18
2017-07-29 08:08



:w - Escribir un archivo.

!sudo - Llamar al comando sudo de shell.

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 la entrada estándar y lo escribe en un archivo representado por%. Sin embargo, esto solicitará volver a cargar el archivo (presione L para cargar los cambios en vim):

enlace tutorial


16
2018-06-04 06:02



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

Añádelo 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 guinda del pastel: llama también al edit comando para volver a cargar el búfer y luego evitar mensajes como el buffer ha cambiado. <bar> es como escribir el tubo símbolo para separar dos comandos aquí.

Espero eso ayude. Ver también para otros problemas:


3
2018-01-13 07:12



Me gustaría sugerir otro enfoque para el "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.

Esto es más bashenfoque centrado que un vim-uno así que no a todos les gustaría.

Por supuesto:

  • hay casos de uso en los que fallará (cuando el propietario del archivo no está 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, yo uso tail o cat para archivos pequeños)

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


1
2018-02-28 18:21