vim sudo

¿Cómo funciona el truco de vim “write with sudo”?



(6)

Muchos de ustedes probablemente han 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 %

La cosa es que no entiendo exactamente lo que está sucediendo aquí.

Ya he pensado 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 |:!|.

Así pasa todas las líneas como entrada estándar.

La parte !sudo tee parte tee con privilegios de administrador.

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

tl; dr ¿Podría alguien ayudarme a diseccionar este comando?


En :w !sudo tee % ...

% significa "el archivo actual"

Como eugene y señaló , % 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 , reemplace las apariciones de foo con bar ". Si resalta un texto antes de escribir :s , verá que las líneas resaltadas ocupan el lugar de % como su rango de sustitución.

:w no está actualizando tu archivo

Una parte confusa de este truco es que podría pensar :w está modificando su archivo, pero no lo está. Si abrió y modificó file1.txt , luego ejecutó :w file2.txt , sería un "guardar como"; file1.txt no se modificaría, pero el contenido del búfer actual 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 a sudo, es :w no 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, utilizamos tee .

Entendiendo tee

En cuanto al tee , imagínese el comando tee 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 la 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'' ps -ax | tee processes.txt | grep ''foo'' ps -ax | tee processes.txt | grep ''foo'' , la lista de procesos se escribirá en un archivo de texto y se pasará a grep .

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

(Diagrama creado con Asciiflow .)

Vea la página de manual de tee para más información.

Tee como un hack

En la situación que describe tu 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 poder llamarlo con sudo .

Haciendo este truco fácil

Puede agregar esto a su .vimrc para hacer este truco fácil de usar: simplemente escriba :w!! .

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

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


En la línea de comando ejecutada, % representa el nombre del 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 descubrió :w !cmd canaliza el contenido del búfer actual a otro comando. Lo que hace tee 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 está siendo root . Otro comando que se puede usar para esto es dd :

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

Como método abreviado, 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.


Esto también funciona bien:

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

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

AVISO :

" debe usar en lugar de '' porque queremos que % se expanda antes de pasar a shell.


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

etc/vim/vimrc 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 debe estar asociado en la línea de comandos.
  • w!! : el atajo en si mismo.
  • execute ''...'' : un comando que ejecuta la siguiente cadena.
  • silent! : ejecutalo 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 comando de edit para volver a cargar el búfer y luego evitar mensajes como el búfer ha cambiado . <bar> es cómo escribir el símbolo de canalización para separar dos comandos aquí.

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


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

En lugar de recibir un permission denied , y tener que escribir :w!! , Me parece más elegante tener un comando vim condicional que haga 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 enfoque más centrado en bash que un vim -oneo para que no todos puedan gustarle.

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 puede editarse de todos modos)
  • no tiene sentido cuando se utiliza vim para leer solo un archivo (en lo que a mí respecta, uso tail o cat para archivos pequeños)

Pero encuentro que esto trae una experiencia de usuario de desarrollo mucho mejor, que es algo que IMHO tiende a olvidarse cuando se usa bash . :-)


:w - Escribe un archivo.

!sudo - Llama 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 la entrada estándar y la escribe en un archivo representado por%. Sin embargo, esto le indicará que vuelva a cargar el archivo (presione L para cargar los cambios en vim):

enlace tutorial