¿Por qué sudo cat da un permiso denegado pero sudo vim funciona bien?

86

Estoy tratando de automatizar la adición de una fuente de repositorio en el archivo pacman.conf de mi arco, pero usando el echocomando en mi script de shell. Sin embargo, falla así: -

sudo echo "[archlinuxfr]" >> /etc/pacman.conf
sudo echo "Server = http://repo.archlinux.fr/\$arch" >> /etc/pacman.conf
sudo echo " " >> /etc/pacman.conf

-bash: /etc/pacman.conf: Permission denied

Si realizo cambios en /etc/pacman.conf manualmente usando vim, haciendo

sudo vim /etc/pacman.conf

y salir de vim con :wq, todo funciona bien y mi pacman.conf se ha actualizado manualmente sin quejas de "Permiso denegado".

¿Por qué esto es tan? ¿Y cómo llego sudo echoal trabajo? (por cierto, intenté usar sudo cattambién pero falló con el permiso denegado también)

Calvin Cheng
fuente
3
posible duplicado de ¿Cómo uso sudo para redirigir la salida a una ubicación en la que no tengo permiso para escribir?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Respuestas:

51

El problema es que la redirección está siendo procesada por su shell original, no por sudo. Los shells no son capaces de leer la mente y no saben que ese particular >>está destinado a él sudoy no a él.

Necesitas:

  1. cite la redirección (por lo que se pasa a sudo)
  2. y use sudo -s(de modo que sudouse un shell para procesar la redirección citada).
geekosaurio
fuente
1
Así que hacerlo en dos pasos como este (1) sudo -s(2) echo "# test" >> /etc/pacman.conf funciona. Pero, ¿es posible ejecutar esto en una sola línea?
Calvin Cheng
15
sudo -s 'echo "# test" >>/etc/pacman.conf'es lo que estaba tratando de transmitirles.
geekosaur
2
En realidad, lo intenté después de leer su respuesta anterior, pero obtuve un error extraño como este: sudo -s 'echo "# test" >> /etc/pacman.conf' /bin/bash: echo "# test" >> /etc/pacman.conf: No such file or directorypor eso probé posteriormente el proceso manual de 2 pasos.
Calvin Cheng
16
Ah ..... un último intento de esta manera funcionó -echo 'echo "# test" >> /etc/pacman.conf' | sudo -s
Calvin Cheng
1
¿Cómo / dónde averiguo sobre la sudodesactivación de la configuración -s? visudo?
Calvin Cheng
127

Como explicó @geekosaur, el shell realiza la redirección antes de ejecutar el comando. Cuando escribe esto:

sudo foo >/some/file

Su proceso de shell actual hace una copia de sí mismo que primero intenta abrirse /some/filepara escritura, luego hace que ese descriptor de archivo sea su salida estándar y solo entonces se ejecuta sudo.

Si está permitido (las configuraciones de sudoer a menudo impiden ejecutar shells), puede hacer algo como esto:

sudo bash -c 'foo >/some/file'

Pero encuentro que una buena solución en general es usar en | sudo teelugar de >y en | sudo tee -alugar de >>. Eso es especialmente útil si la redirección es la única razón que necesito sudoen primer lugar; después de todo, ejecutar procesos innecesariamente como root es precisamente lo que sudose creó para evitar. Y correr echocomo root es una tontería.

echo '[archlinuxfr]' | sudo tee -a /etc/pacman.conf >/dev/null
echo 'Server = http://repo.archlinux.fr/$arch' | sudo tee -a /etc/pacman.conf >/dev/null
echo ' ' | sudo tee -a /etc/pacman.conf >/dev/null

Agregué > /dev/nullal final porque teeenvía su salida tanto al archivo con nombre como a su propia salida estándar, y no necesito verlo en mi terminal. (El teecomando actúa como un conector "T" en una tubería física, que es de donde obtiene su nombre). Y cambié a comillas simples ( '... ') en lugar de dobles ( "... ") para que todo sea literal y no tener que poner una barra invertida delante de la $en $arch. (Sin las comillas o la barra invertida, $archsería reemplazado por el valor del parámetro de shell arch, que probablemente no existe, en cuyo caso $archse reemplaza por nada y simplemente desaparece).

Entonces eso se encarga de escribir en archivos como root usando sudo. Ahora, para una larga digresión sobre las formas de generar texto que contiene una nueva línea en un script de shell. :)

Para BLUF, como dicen, mi solución preferida sería simplemente introducir un documento aquí en el sudo teecomando anterior ; entonces no hay necesidad de cato echoo printfo cualquier otro comando en absoluto. Las comillas simples se han trasladado a la introducción centinela <<'EOF', pero tienen el mismo efecto allí: el cuerpo se trata como texto literal, por lo que $archse deja solo:

sudo tee -a /etc/pacman.conf >/dev/null <<'EOF'
[archlinuxfr]
Server = http://repo.archlinux.fr/$arch

EOF

Pero aunque así es como lo haría, hay alternativas. A continuación, presentamos algunos:

Puede quedarse con uno echopor línea, pero agruparlos todos en una subcapa, por lo que solo tiene que agregar al archivo una vez:

(echo '[archlinuxfr]'
 echo 'Server = http://repo.archlinux.fr/$arch'
 echo ' ') | sudo tee -a /etc/pacman.conf >/dev/null

Si agrega -ea echo(y está usando un shell que admite esa extensión que no es POSIX), puede incrustar nuevas líneas directamente en la cadena usando \n:

# NON-POSIX - NOT RECOMMENDED
echo -e '[archlinuxfr]\nServer = http://repo.archlinux.fr/$arch\n ' | 
  sudo tee -a /etc/pacman.conf >/dev/null

Pero como dice arriba, ese no es un comportamiento especificado por POSIX; su shell podría simplemente repetir un literal -eseguido de una cadena con un montón de literales en \nsu lugar. La forma POSIX de hacerlo es usar en printflugar de echo; trata automáticamente su argumento como lo echo -ehace, pero no agrega automáticamente una nueva línea al final, por lo que también debe agregar un extra \nallí:

printf '[archlinuxfr]\nServer = http://repo.archlinux.fr/$arch\n \n' | 
  sudo tee -a /etc/pacman.conf >/dev/null

Con cualquiera de esas soluciones, lo que el comando obtiene como una cadena de argumento contiene la secuencia de dos caracteres \n, y depende del programa de comando en sí (el código dentro de printfo echo) traducir eso en una nueva línea. En muchos shells modernos, tiene la opción de usar comillas ANSI $'... ', que traducirán secuencias como \nen líneas nuevas literales antes de que el programa de comando vea la cadena. Eso significa que este tipo de cadenas funcionan con cualquier comando que sea, incluyendo a secas -e-menos echo:

echo $'[archlinuxfr]\nServer = http://repo.archlinux.fr/$arch\n ' | 
  sudo tee -a /etc/pacman.conf >/dev/null

Pero, aunque son más portátiles que echo -e, las citas ANSI siguen siendo una extensión que no es POSIX.

Y nuevamente, si bien esas son todas las opciones, prefiero la tee <<EOFsolución directa anterior.

Mark Reed
fuente
¡Interesante! ¿Podría agregar una nota que explique la razón por la que también se fragmenta el archivo en / dev / null?
knite
sudo bash -c 'foo >/some/file'funciona para mí en osx :-)
kayochin
Prefiero esta respuesta porque funciona con texto de varias líneas. cat <<EOF | sudo tee -a /some/file > /dev/null ...
ltvan
Esa es efectivamente mi última respuesta, excepto que ha agregado un catproceso extraño . :)
Mark Reed
17

http://www.innovationsts.com/blog/?p=2758

Como las instrucciones no son tan claras arriba, estoy usando las instrucciones de esa publicación de blog. Con ejemplos para que sea más fácil ver lo que necesita hacer.

$ sudo cat /root/example.txt | gzip> /root/example.gz
-bash: /root/example.gz: Permiso denegado

Observe que es el segundo comando (el comando gzip) en la canalización el que causa el error. Ahí es donde entra en juego nuestra técnica de usar bash con la opción -c.

$ sudo bash -c 'cat /root/example.txt | gzip> /root/example.gz '
$ sudo ls /root/example.gz
/root/example.gz

Podemos ver en la salida del comando ls que la creación del archivo comprimido tuvo éxito.

El segundo método es similar al primero en que le estamos pasando una cadena de comando a bash, pero lo estamos haciendo en una canalización a través de sudo.

$ sudo rm /root/example.gz
$ echo "cat /root/example.txt | gzip> /root/example.gz" | sudo bash
$ sudo ls /root/example.gz
/root/example.gz

nelaaro
fuente
1
Para un comando tan simple, podría ser un poco mejor usar sh en lugar de bash. Por ejemplo, sudo sh -c 'cat /root/example.txt | gzip> /root/example.gz '. Pero por lo demás, buenas explicaciones, +1.
Bryce
7
sudo bash -c 'echo "[archlinuxfr]" >> /etc/pacman.conf'
Peyman Mahdian
fuente
1

PASO 1 crea una función en un archivo bash ( write_pacman.sh)

#!/bin/bash

function write_pacman {
 tee -a /etc/pacman.conf > /dev/null << 'EOF'
  [archlinuxfr]
  Server = http://repo.archlinux.fr/\$arch
EOF
}

'EOF'no interpretará la $archvariable.

Archivo bash fuente STE2

$ source write_pacman.sh

PASO 3 ejecutar la función

$ write_pacman
prayagupd
fuente
¿Para qué es la cotización en EOF?
bilabila
0

añadir archivos (sudo cat):

cat <origin-file> | sudo tee -a <target-file>

añadir eco al archivo (sudo echo):

echo <origin> | sudo tee -a <target-file>

(EXTRA) ignore la salida:

echo >origin> | sudo tee -a <target-file> >/dev/null
diogowalterdefreitas
fuente