Redirigir stdout a un archivo en el que no tiene permiso de escritura

113

Cuando intentas modificar un archivo sin tener permisos de escritura, obtienes un error:

> touch /tmp/foo && sudo chown root /tmp/foo
> echo test > /tmp/foo
zsh: permission denied: /tmp/foo

Sudoing no ayuda, porque ejecuta el comando como root, pero el shell maneja la redirección stdout y abre el archivo como usted de todos modos:

> sudo echo test > /tmp/foo
zsh: permission denied: /tmp/foo

¿Hay una manera fácil de redirigir stdout a un archivo en el que no tiene permiso para escribir, además de abrir un shell como root y manipular el archivo de esa manera?

> sudo su
# echo test > /tmp/foo
Michael Mrozek
fuente
2
Responda a una pregunta similar de StackOverflow stackoverflow.com/questions/82256/…
Cristian Ciupitu
¿Cómo puede no tener permiso para un archivo que creó usted mismo en tmp? ¿Es por umask?
k961
@ k961 Solía chowncambiar el propietario; fue solo un ejemplo
Michael Mrozek

Respuestas:

116

Sí, utilizando tee. Entonces se echo test > /tmp/fooconvierte

echo test | sudo tee /tmp/foo

También puedes agregar ( >>)

echo test | sudo tee -a /tmp/foo
Gert
fuente
27
Tee también saldrá a stdout; a veces no quieres que los contenidos llenen la pantalla. Para solucionar esto, haga lo siguienteecho test | sudo tee /tmp/foo > /dev/null
Shawn J. Goff,
¿Cómo lo harás con heredoc?
JohnDoea
26

Para reemplazar el contenido del archivo con la salida de echo(como el >operador de redirección de shell).

echo test | sudo dd of=/tmp/foo

Para escribir en el archivo (al principio, aunque puede usar seekpara generar en diferentes desplazamientos) sin truncar (como el 1<>operador de shell Bourne):

echo test | sudo dd of=/tmp/foo conv=notrunc

Para adjuntar al archivo (como >>), con GNU dd:

echo test | sudo dd of=/tmp/foo oflag=append conv=notrunc

Ver también GNU dd's conv=exclpara evitar clobbering un archivo existente (como la set -o noclobberde conchas POSIX) y conv=nocreatpor todo lo contrario (sólo actualizar un archivo existente).

Chris Jepeway
fuente
¡inteligente! Esto alivia la necesidad echo test | sudo tee /tmp/foo >/dev/nullde descartar la salida.
Adam Katz
2
Puede que tenga que recuperar eso; ddno es confiable para eso a menos que esté usando opciones oscuras solo de GNU iflag=fullblock oflag=fullblock, que eliminan la elegancia de esta respuesta. Yo me quedo con tee.
Adam Katz
55
dd es confiable con el bs no oscuro = 1
umeboshi
2
@umeboshi Pero confiable solo si tienes la experiencia suficiente para saber exactamente lo que estás haciendo. Porque ddpuede ser bastante peligroso (si no quiere decir: devastador) si solo se cometió un pequeño error. Entonces, para los nuevos usuarios, prefiero recomendar el teemétodo para estar en la costa segura.
syntaxerror
1
@AdamKatz, en el caso de dd of=filesolo (sin count/ skip...), es confiable. iflag=fullblockno es necesario porque aquí ddescribe en la salida lo que ha leído en la entrada. No importa si no fueron bloques completos.
Stéphane Chazelas
12

tee es probablemente la mejor opción, pero dependiendo de su situación, algo como esto puede ser suficiente:

sudo sh -c 'echo test > /tmp/foo'
phunehehe
fuente
1
3 problemas: en evallugar de un simple sh -c; -i, que cambiará su directorio de trabajo; ejecutar todo el comando como root, lo que podría cambiar su comportamiento o introducir un riesgo innecesario. ¿Por qué esto alguna vez recibió un voto positivo?
44
no lo hiciste, pero es engañoso, generalmente malo y quizás incluso peligroso.
5

Si bien estoy de acuerdo, esa | sudo teees la forma canónica, a veces sed (aquí suponiendo GNU sed) puede funcionar:

cat sudotest 
line 1

sudo sed -i '1iitest' sudotest && cat sudotest 
itest
line 1

sudo sed -i '$aatest' sudotest && cat sudotest 
itest
line 1
atest

-imodifica el archivo en su lugar. 1isignifica insertar antes de la línea 1. $asignifica agregar después de la última línea.

O copiar a xclipboard:

somecommand | xclip
sudo gedit sudotest
move cursor to desired place, click middle mouse button to insert, save
usuario desconocido
fuente
1
Esto está redactado como un enigma chino antiguo. No puedo ver para entender cómo esto obtuvo tantos votos positivos. ( hrmph )
syntaxerror
1
@syntaxerror: Señor, lo siento, no soy un hablante nativo de inglés. Si especifica lo que no está claro para usted, podría intentar mejorar mi respuesta. En comparación con 65 votos a favor para Gert, 4 no parece ser tantos votos a favor, ¿no crees?
usuario desconocido
Bueno, estaría bastante satisfecho con 4 :-D Tu inglés no es malo en absoluto. Está redactado en una especie de nerdspeak técnico conciso. Debo adivinar que eres un codificador. Los codificadores entre sí se entenderán perfectamente de esa manera, pero un no codificador debe pensar que está redactado como ... como se dijo.
syntaxerror
Tenga en cuenta que en sed -irealidad no modifica el archivo en su lugar : crea un archivo temporal y lo renombra al salir. Por lo tanto, no podrá hacer algo como tail -f ...en el archivo original y ver la salida usando sed -i ...mientras la tubería se está ejecutando
Andrew Henle
@AndrewHenle: Sí, ya que el tamaño puede aumentar o reducirse, y dado que ese es probablemente el caso para la mayoría de las invocaciones de sed, y ni siquiera puede, afaik, escribir en la misma ubicación en los SSD, es solo una operación pseudo 'en el lugar' . Como hablante no nativo de inglés, ¿puedo pedir una breve expresión, que probablemente no se malinterprete? ¿Justo -i creates a new file of same nameo hay algo más compacto? Supongo que me gusta in place, porque explica el i. La página de manual de GNU-sed lo llama en su lugar también y el largo de la bandera es --in-place.
Usuario desconocido
2

He estado dando vueltas en el fondo de mis ideas para un problema similar, y se me ocurrieron las siguientes soluciones:

  • sudo uncatdonde uncates un programa que lee la entrada estándar y la escribe en el archivo nombrado en la línea de comando, pero aún no lo he escrito uncat.

  • sudocatla variante de sudoediteso que aún no he escrito que hace un limpiador sudo cato sudo uncat.

  • o este pequeño truco de usar sudoeditcon un EDITOR que es un script de shell

    #!/bin/sh
    # uncat
    cat > "$1"

    que puede invocarse como |sudo ./uncat fileo | EDITOR=./uncat sudoeditpero tiene efectos secundarios interesantes.

hildred
fuente
cattiene una lista de archivos para con gato innata, por lo tanto, UNCAT debe tener una lista de archivos a la ONU con gato inate a. Tendría que usar magia para decidir cuánto poner en cada archivo. Nombre alternativo incluyen dog, to-file, redirect.
ctrl-alt-delor
No puedo pensar en ninguna razón por la que quisiera uncatcuando lo tengo tee.
Comodín el
1
Bueno, teetiene el inconveniente trivial en el que escribe su stdin en su stdout, que se mitiga trivialmente al redirigir el stdout /dev/null. Otras alternativas incluyen dd of=/tmp/foo(mencionado en otra respuesta), que escribe información de estado en stderr, y cp /dev/stdin /tmp/foo.
Scott
1

Use esponja del paquete moreutils. Tiene la ventaja de que no escribe en stdout.

echo test | sudo sponge /tmp/foo

Use la opción -a para agregar un archivo en lugar de sobrescribirlo.

Hontvári Levente
fuente
1
No estoy seguro de qué ventaja no escribir en stdout presenta, pero podría simplemente redirigir la salida a /dev/nullsi fuera un problema
Michael Mrozek
1
Por supuesto, puedo redirigir a / dev / null, pero el comando es más fácil de leer y escribir sin la redirección. La ventaja de no escribir en stdout es que mi terminal no está llena de basura.
Hontvári Levente
1
No instalaría un paquete para ahorrar una redirección, pero uso esponja regularmente, por lo que ya está allí.
Hontvári Levente
0

El error proviene del orden en que el shell hace las cosas.

La redirección se maneja incluso antes de que se ejecute el shell sudoy, por lo tanto, se realiza con los permisos del usuario con el que está trabajando actualmente. Como no tiene permisos de escritura para crear / truncar el objetivo de la redirección, obtiene un permission deniederror del shell.

La solución es garantizar que el archivo de salida se cree con la identidad que se le proporcionó, por sudoejemplo, con tee:

$ generate_output | sudo tee target_file
Kusalananda
fuente