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:
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 |:!|.
entonces pasa todas las líneas como entrada estándar.
La !sudo tee
parte llama tee
con 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?sudo
se 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 paratee
que 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/bar
significa " en el archivo actual , reemplace las ocurrencias defoo
conbar
". algo de texto antes de escribir:s
, verá que las líneas resaltadas toman el lugar%
como su rango de sustitución).:w
no está actualizando su archivoUna parte confusa de este truco es que podría pensar que
:w
está modificando su archivo, pero no lo es. 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 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 !cat
solo mostrará los contenidos.Si Vim no se ejecutó con acceso sudo,
: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, usamostee
.Camiseta de comprensión
En cuanto a
tee
, imagine eltee
comando 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
tee
página del manual para más información.Tee como un truco
En la situación que describe su pregunta, usar
tee
es un hack porque estamos ignorando 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 lo estamos utilizandotee
como una forma alternativa de escribir un archivo y para poder llamarlosudo
.Hacer este truco fácil
Puedes agregar esto a tu
.vimrc
para hacer que este truco sea fácil de usar: solo escribe:w!!
.La
> /dev/null
parte arroja explícitamente la salida estándar, ya que, como dije, no necesitamos pasar nada a otro comando canalizado.fuente
tee
por 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é llamarsponge
que 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 (encmd
cambio, se bifurca )? Quizás algo que sea más obvio sería usar alguna variante de trabajo ensh -c ">"
lugar detee
.sponge
es parte delmoreutils
paquete en casi todas las distribuciones, excepto en las distribuciones basadas en Debian.moreutils
tiene algunas herramientas bastante buenas que están a la par con herramientas más comunes comoxargs
ytee
.cat
ejecuta 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 !cmd
canaliza el contenido del búfer actual a otro comando. Lo quetee
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 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?help
comando 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 enwildmode
consecuencia)cmap w!! w !sudo tee % > /dev/null
en 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/file
lo 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
tee
con/usr/bin/tee
para 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 mensajesNULL
para hacer un comando limpio<bar> edit!
: este truco es la guinda del pastel: también llama aledit
comando 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
sudo
al abrir mi archivo" :En lugar de recibir un
permission denied
, y tener que escribir:w!!
, me parece más elegante tener unvim
comando condicional que lo hacesudo vim
si 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
bash
enfoque más centrado quevim
uno, por lo que no a todos les gustaría.Por supuesto:
root
pero lo requieresudo
, pero la función se puede editar de todos modos)vim
para leer solo un archivo (en lo que a mí respecta, usotail
ocat
para 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
root
se 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-askpass
la solicitud de la contraseña de sudo.fuente