Perché sudo cat dà un'autorizzazione negata ma sudo vim funziona bene?


87

Sto cercando di automatizzare l'aggiunta di una fonte di repository nel file pacman.conf del mio arch ma utilizzando il echocomando nel mio script di shell. Tuttavia, fallisce in questo modo: -

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

Se apporto modifiche a /etc/pacman.conf manualmente usando vim, facendo

sudo vim /etc/pacman.conf

e uscendo da vim con :wq, tutto funziona bene e il mio pacman.conf è stato aggiornato manualmente senza reclami "Permission denied".

Perché è così? E come arrivo sudo echoa lavorare? (a proposito, ho provato a usare sudo catanche io ma non è riuscito con l'autorizzazione negata pure)


Risposte:


52

Il problema è che il reindirizzamento viene elaborato dalla shell originale, non da sudo. Le conchiglie non sono in grado di leggere la mente e non sanno che quel particolare >>è pensato per il sudoe non per esso.

Devi:

  1. cita il reindirizzamento (quindi viene passato a sudo)
  2. e usa sudo -s(in modo che sudoutilizzi una shell per elaborare il reindirizzamento citato.)

1
Quindi farlo in due passaggi come questo (1) sudo -s(2) echo "# test" >> /etc/pacman.conf funziona. Ma è possibile eseguirlo in una sola riga?
Calvin Cheng

15
sudo -s 'echo "# test" >>/etc/pacman.conf'è quello che stavo cercando di trasmettervi.
geekosaur

2
In realtà, l'ho provato dopo aver letto la tua risposta sopra ma ho ricevuto uno strano errore come questo: - sudo -s 'echo "# test" >> /etc/pacman.conf' /bin/bash: echo "# test" >> /etc/pacman.conf: No such file or directorymotivo per cui ho successivamente provato il processo manuale in 2 passaggi.
Calvin Cheng

16
Ah ..... un ultimo tentativo in questo modo ha funzionato -echo 'echo "# test" >> /etc/pacman.conf' | sudo -s
Calvin Cheng

1
come / dove trovo informazioni sulla sudodisabilitazione della configurazione -s? visudo?
Calvin Cheng

129

Come spiegato da @geekosaur, la shell esegue il reindirizzamento prima di eseguire il comando. Quando digiti questo:

sudo foo >/some/file

Il tuo attuale processo di shell crea una copia di se stesso che prima prova ad aprirsi /some/fileper la scrittura, poi se ha successo, fa di quel descrittore di file il suo output standard e solo se ha successo lo esegue sudo. Questo sta fallendo nel primo passaggio.

Se sei autorizzato (le configurazioni di sudoer spesso precludono l'esecuzione di shell), puoi fare qualcosa del genere:

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

Ma trovo che una buona soluzione in generale sia usare al | sudo teeposto di >e | sudo tee -ainvece di >>. Ciò è particolarmente utile se il reindirizzamento è l'unico motivo di cui ho bisogno sudoin primo luogo; dopotutto, eseguire inutilmente processi come root è esattamente ciò che è sudostato creato per evitare. E correre echocome root è semplicemente stupido.

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

Ho aggiunto > /dev/nullalla fine perché teeinvia il suo output sia al file denominato che al proprio output standard, e non ho bisogno di vederlo sul mio terminale. (Il teecomando si comporta come un connettore a "T" in una pipeline fisica, da cui prende il nome.) E sono passato alle virgolette singole ( '... ') invece delle doppie ( "... ") in modo che tutto sia letterale e io non è stato necessario inserire una barra rovesciata davanti a $in $arch. (Senza le virgolette o la barra rovesciata, $archverrebbe sostituito dal valore del parametro della shell arch, che probabilmente non esiste, nel qual caso $archviene sostituito da niente e svanisce.)

Quindi questo si occupa di scrivere sui file come root usando sudo. Ora per una lunga digressione sui modi per produrre testo contenente una nuova riga in uno script di shell. :)

Per BLUF, come si suol dire, la mia soluzione preferita sarebbe semplicemente inserire un here-document nel sudo teecomando precedente ; quindi non c'è bisogno di cator echoor printfo altri comandi. Le virgolette singole sono state spostate nell'introduzione della sentinella <<'EOF', ma lì hanno lo stesso effetto: il corpo viene trattato come testo letterale, quindi $archviene lasciato solo:

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

EOF

Ma mentre è così che lo farei, ci sono alternative. Eccone alcuni:

Puoi restare con uno echoper riga, ma raggruppali tutti insieme in una subshell, quindi devi solo aggiungerli al file una volta:

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

Se aggiungi -ea echo(e stai usando una shell che supporta quell'estensione non POSIX), puoi incorporare newline direttamente nella stringa usando \n:

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

Ma come si dice sopra, questo non è un comportamento specificato da POSIX; la tua shell potrebbe semplicemente echeggiare un letterale -eseguito da una stringa con un mucchio di \ns letterali . Il modo POSIX per farlo è usare printfinvece di echo; tratta automaticamente il suo argomento come echo -efa, ma non aggiunge automaticamente una nuova riga alla fine, quindi devi aggiungere un extra anche \nlì:

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

Con una di queste soluzioni, ciò che il comando ottiene come stringa di argomento contiene la sequenza di due caratteri \ne spetta al programma di comando stesso (il codice all'interno di printfo echo) tradurlo in una nuova riga. In molte shell moderne, hai la possibilità di utilizzare virgolette ANSI $'... ', che tradurranno sequenze come \nin lettere a capo prima che il programma di comando veda la stringa. Ciò significa che tali stringhe funzionano con qualsiasi comando, incluso il semplice old -e-less echo:

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

Ma, sebbene più portabili di echo -e, le virgolette ANSI sono ancora un'estensione non POSIX.

E ancora, sebbene quelle siano tutte opzioni, preferisco la tee <<EOFsoluzione diretta sopra.


Interessante! Potresti aggiungere una nota che spieghi il motivo per cui il file viene anche suddiviso in blocchi / dev / null?
Knite

sudo bash -c 'foo >/some/file'funziona per me su osx :-)
kayochin

Preferisco questa risposta perché funziona con testo multilinea. cat <<EOF | sudo tee -a /some/file > /dev/null ...
ltvan

Questa è effettivamente la mia ultima risposta, tranne per il fatto che hai aggiunto un catprocesso estraneo . :)
Mark Reed

17

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

Poiché le istruzioni sopra non sono così chiare, sto usando le istruzioni di quel post sul blog. Con esempi così è più facile vedere cosa devi fare.

$ sudo cat /root/example.txt | gzip> /root/example.gz
-bash: /root/example.gz: autorizzazione negata

Si noti che è il secondo comando (il comando gzip) nella pipeline che causa l'errore. È qui che entra in gioco la nostra tecnica di usare bash con l'opzione -c.

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

Possiamo vedere dall'output del comando ls che la creazione del file compresso è riuscita.

Il secondo metodo è simile al primo in quanto stiamo passando una stringa di comando a bash, ma lo stiamo facendo in una pipeline tramite 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


1
Per un comando così semplice, potrebbe essere leggermente meglio usare sh invece di bash. Ad esempio, sudo sh -c 'cat /root/example.txt | gzip> /root/example.gz '. Ma per il resto, buone spiegazioni, +1.
bryce


1

PASSO 1 crea una funzione in un file 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'non interpreterà la $archvariabile.

File bash sorgente STE2

$ source write_pacman.sh

FASE 3 eseguire la funzione

$ write_pacman

a cosa serve la citazione su EOF?
bilabila

0

aggiungi file (sudo cat):

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

aggiungi echo al file (sudo echo):

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

(EXTRA) ignora l'uscita:

echo >origin> | sudo tee -a <target-file> >/dev/null
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.