¿Cómo uso sudo para redirigir la salida a una ubicación en la que no tengo permiso para escribir?

881

Me dieron acceso a sudo en una de nuestras cajas de desarrollo RedHat Linux, y parece que a menudo necesito redirigir la salida a una ubicación a la que normalmente no tengo acceso de escritura.

El problema es que este ejemplo artificial no funciona:

sudo ls -hal /root/ > /root/test.out

Acabo de recibir la respuesta:

-bash: /root/test.out: Permission denied

¿Cómo puedo hacer que esto funcione?

Jonathan
fuente
1
use chmod u + w filename
Szabolcs Dombi
@DombiSzabolcs ¿Sugiere que primero cree el archivo como sudo y luego me dé permiso? Buena idea.
Jonathan
En muchas situaciones, terminas aquí porque preguntaste "¿por qué me niegan el permiso?" A veces, la respuesta es que necesita crear un archivo como root(en cuyo caso proceda a leer las respuestas) pero muy a menudo, simplemente necesita crear el archivo en otro lugar, como usted mismo.
tripleee
1
Después de luchar con estas respuestas, finalmente elijo redirigir a un archivo temporal y sudo moverlo a destino.
TingQian LI
Ver también stackoverflow.com/questions/84882/…
Nate Eldredge

Respuestas:

1233

Su comando no funciona porque la redirección la realiza su shell, que no tiene permiso para escribir /root/test.out. La redirección de la salida no es realizada por sudo.

Hay múltiples soluciones:

  • Ejecute un shell con sudo y dele el comando usando la -copción:

    sudo sh -c 'ls -hal /root/ > /root/test.out'
    
  • Cree un script con sus comandos y ejecútelo con sudo:

    #!/bin/sh
    ls -hal /root/ > /root/test.out
    

    Ejecutar sudo ls.sh. Vea la respuesta de Steve Bennett si no desea crear un archivo temporal.

  • Inicie un shell con sudo -sluego ejecute sus comandos:

    [nobody@so]$ sudo -s
    [root@so]# ls -hal /root/ > /root/test.out
    [root@so]# ^D
    [nobody@so]$
    
  • Use sudo tee(si tiene que escapar mucho cuando usa la -copción):

    sudo ls -hal /root/ | sudo tee /root/test.out > /dev/null
    

    La redirección a /dev/nulles necesaria para evitar que el tee salga a la pantalla. Para agregar en lugar de sobrescribir el archivo de salida ( >>), use tee -ao tee --append(el último es específico de los coreutils de GNU ).

Gracias a Jd , Adam J. Forster y Johnathan por la segunda, tercera y cuarta solución.

Cristian Ciupitu
fuente
1
Hay una gran respuesta que te dice cómo redirigir STDERR y STDOUT por separado aquí: stackoverflow.com/questions/692000/… ... básicamenteperl -e 'print "STDIN\n"; print STDERR "STDERR\n"; ' > >( tee stdout.log ) 2> >( tee stderr.log >&2 )
errant.info
2
Querrá hacer 'sudo -E ...' para usar variables en el comando descascarado (cuando se usa esto en un script, por ejemplo).
Urhixidur
3
Redirigir la salida de tee a / dev / null probablemente no sea necesario en muchos casos en los que hacer eco de la salida a la pantalla es inofensivo. Por ejemplo, cuando se trata solo con la salida de comandos regulares o el contenido de pequeños archivos de texto.
thomasrutter
105

Alguien aquí ha sugerido sudoing tee:

sudo ls -hal /root/ | sudo tee /root/test.out > /dev/null

Esto también podría usarse para redirigir cualquier comando a un directorio al que no tenga acceso. Funciona porque el programa tee es efectivamente un programa de "eco a un archivo", y la redirección a / dev / null es evitar que también salga a la pantalla para mantenerlo igual que el ejemplo artificial original anterior.

Jonathan
fuente
55
En muchos casos, es decir, si el usuario normal tiene permisos para ejecutar el comando y "solo" no puede escribir en el archivo de salida deseado, el primero sudo(es decir, para el comando en sí) podría omitirse
Hagen von Eitzen
81

Un truco que descubrí yo mismo fue

sudo ls -hal /root/ | sudo dd of=/root/test.out
rhlee
fuente
16
sudo ddes mejor que los sudo tee /root/file > /dev/nullejemplos anteriores!
kristianlm
14
dd no es un comando extraño y oscuro. Se utiliza siempre que necesite copiar grandes cantidades de datos con almacenamiento en búfer entre dos dispositivos de bloque. La sintaxis es bastante simple, ddes el nombre del comando, of=/root/test.outes el argumento que indica ddcuáles son los archivos de salida.
rhlee
14
@steve Todo es 'oscuro' hasta que aprendes lo que es. ofsignifica archivo de salida y ddes una herramienta muy popular utilizada tanto en Linux como en OSX (principalmente para escribir imágenes en discos). Este es un buen truco seguro.
blockloop
12
ddes probablemente igualmente útil como teeaquí. En ambos casos, está utilizando un comando común y conocido para un propósito que, aunque es ligeramente diferente de su propósito original, aún es conocido y está bien documentado. Si bien ddes bueno para copiar grandes cantidades de datos, no es malo con pequeñas cantidades. Aquí tiene la ventaja de no hacer eco de la salida a la salida estándar también.
thomasrutter
26
Cada vez que escriba palabras sudo ddlado de la otra, quiere hacer muy, muy seguro de que los argumentos que siguen son correctas (especialmente teniendo en cuenta su sintaxis no estándar). No lo llaman "destructor de discos" por nada ...
ali_m
45

El problema es que el comando se ejecuta bajo sudo, pero la redirección se ejecuta bajo su usuario. Esto lo hace el shell y hay muy poco que pueda hacer al respecto.

sudo command > /some/file.log
`-----v-----'`-------v-------'
   command       redirection

Las formas habituales de evitar esto son:

  • Envuelva los comandos en un script que llame bajo sudo.

    Si los comandos y / o el archivo de registro cambian, puede hacer que el script los tome como argumentos. Por ejemplo:

    sudo log_script command /log/file.txt
    
  • Llame a un shell y pase la línea de comando como parámetro con -c

    Esto es especialmente útil para los comandos compuestos únicos. Por ejemplo:

    sudo bash -c "{ command1 arg; command2 arg; } > /log/file.txt"
    
dsm
fuente
23

Otra variación más sobre el tema:

sudo bash <<EOF
ls -hal /root/ > /root/test.out
EOF

O por supuesto:

echo 'ls -hal /root/ > /root/test.out' | sudo bash

Tienen la (pequeña) ventaja de que no es necesario recordar a los argumentos sudoo sh/bash

Steve Bennett
fuente
18

Aclarando un poco sobre por qué es preferible la opción de tee

Suponiendo que tiene el permiso apropiado para ejecutar el comando que crea la salida, si canaliza la salida de su comando a tee, solo necesita elevar los privilegios de tee con sudo y tee directo para escribir (o agregar) al archivo en cuestión.

en el ejemplo dado en la pregunta que significaría:

ls -hal /root/ | sudo tee /root/test.out

para un par de ejemplos más prácticos:

# kill off one source of annoying advertisements
echo 127.0.0.1 ad.doubleclick.net | sudo tee -a /etc/hosts

# configure eth4 to come up on boot, set IP and netmask (centos 6.4)
echo -e "ONBOOT=\"YES\"\nIPADDR=10.42.84.168\nPREFIX=24" | sudo tee -a /etc/sysconfig/network-scripts/ifcfg-eth4

En cada uno de estos ejemplos, está tomando la salida de un comando no privilegiado y escribiendo en un archivo que generalmente solo se puede escribir por raíz, que es el origen de su pregunta.

Es una buena idea hacerlo de esta manera porque el comando que genera la salida no se ejecuta con privilegios elevados. No parece importar aquí, echopero cuando el comando de origen es un script en el que no confías por completo, es crucial.

Tenga en cuenta que puede usar la opción -a para tee para agregar anexar (like >>) al archivo de destino en lugar de sobrescribirlo (like >).

jg3
fuente
Lo siento js3, pero esto ya se ha sugerido (en 2008) y está en la segunda respuesta más alta: stackoverflow.com/a/82553/6910
Jonathan
2
Tienes razón, Jonathan, actualizaré mi respuesta para ampliar las razones por las cuales esta es una opción preferible. Gracias por los útiles comentarios.
jg3
17

Haga que sudo ejecute un shell, así:

sudo sh -c "echo foo > ~root/out"
Penfold
fuente
11

La forma en que abordaría este problema es:

Si necesita escribir / reemplazar el archivo:

echo "some text" | sudo tee /path/to/file

Si necesita agregar al archivo:

echo "some text" | sudo tee -a /path/to/file
Nikola Petkanski
fuente
1
¿Cómo es esto sustancialmente diferente a las respuestas anteriores?
Jonathan
2
No es "sustancialmente diferente", pero aclara el uso distintivo entre reemplazar y agregar al archivo.
jamadagni
1
Esta es la respuesta que copié en mi hoja de trucos.
MortimerCat
5

¿Qué tal escribir un guión?

Nombre de archivo: myscript

#!/bin/sh

/bin/ls -lah /root > /root/test.out

# end script

Luego use sudo para ejecutar el script:

sudo ./myscript

fuente
4

Cada vez que tengo que hacer algo como esto, me convierto en root:

# sudo -s
# ls -hal /root/ > /root/test.out
# exit

Probablemente no sea la mejor manera, pero funciona.

Adam J. Forster
fuente
4

Lo haría de esta manera:

sudo su -c 'ls -hal /root/ > /root/test.out'
Fardjad
fuente
2
En realidad, eso parece un poco más limpio, ya que no necesita especificar explícitamente el shell.
Steve Bennett
1
Un pequeño inconveniente es que ejecuta un proceso adicional ( su): $ sudo su -c 'pstree -sp $$ >/dev/fd/1' init(1)───gnome-terminal(6880)───bash(6945)───sudo(401)───su(402)───bash(410)───pstree(411)
pabouk
3

No es mi intención vencer a un caballo muerto, pero hay demasiadas respuestas aquí que se usan tee, lo que significa que debe redirigir stdouta/dev/null menos que desee ver una copia en la pantalla.

Una solución más simple es usarla catasí:

sudo ls -hal /root/ | sudo bash -c "cat > /root/test.out"

Observe cómo la redirección se coloca entre comillas para que sea evaluada por un shell iniciado en sudolugar de uno que lo ejecuta.

haridsv
fuente
Esto funciona bien. No entiendo por qué tuvo un voto negativo. Votado
Teemu Leisti
44
Creo que no recibe mucho amor porque no es mucho mejor que sudo bash -c "ls -hal /root > /root/test.out". El uso de tee evita la necesidad de un caparazón, mientras que el gato no.
Nick Russo
2

Esto se basa en la respuesta que involucra tee. Para facilitar las cosas, escribí un pequeño script (lo llamo suwrite) y lo puse /usr/local/bin/con +xpermiso:

#! /bin/sh
if [ $# = 0 ] ; then
    echo "USAGE: <command writing to stdout> | suwrite [-a] <output file 1> ..." >&2
    exit 1
fi
for arg in "$@" ; do
    if [ ${arg#/dev/} != ${arg} ] ; then
        echo "Found dangerous argument ‘$arg’. Will exit."
        exit 2
    fi
done
sudo tee "$@" > /dev/null

Como se muestra en el USO en el código, todo lo que tiene que hacer es canalizar la salida a este script seguido del nombre de archivo deseado accesible por el superusuario y automáticamente le pedirá su contraseña si es necesario (ya que incluye sudo).

echo test | suwrite /root/test.txt

Tenga en cuenta que dado que este es un contenedor simple para tee, también aceptará la -aopción de tee para agregar, y también admite la escritura en múltiples archivos al mismo tiempo.

echo test2 | suwrite -a /root/test.txt
echo test-multi | suwrite /root/test-a.txt /root/test-b.txt

También tiene una protección simplista contra la escritura en /dev/dispositivos, lo cual fue una preocupación mencionada en uno de los comentarios en esta página.

jamadagni
fuente
1

¿Tal vez le han dado acceso a sudo solo a algunos programas / rutas? Entonces no hay forma de hacer lo que quieres. (a menos que lo piratee de alguna manera)

Si no es el caso, entonces quizás puedas escribir un script bash:

cat > myscript.sh
#!/bin/sh
ls -hal /root/ > /root/test.out 

Presione ctrl+ d:

chmod a+x myscript.sh
sudo myscript.sh

Espero que sea de ayuda.

usuario15453
fuente
1
sudo at now  
at> echo test > /tmp/test.out  
at> <EOT>  
job 1 at Thu Sep 21 10:49:00 2017  
usuario8648126
fuente
1
Si tiene de sudotodos modos, atno es útil o necesario. sudo sh -c 'echo test >/tmp/test.out'hace lo mismo de manera mucho más eficiente y elegante (pero aún sufre la falla de que probablemente esté ejecutando cosas rootque no necesitan ese privilegio; por lo general, debe evitar los comandos privilegiados cuando pueda).
tripleee