2012-04-13 11 views
66

Sto cercando di automatizzare l'aggiunta di un'origine del repository nel file pacman.conf del mio arco ma utilizzando il comando echo nello script della shell. Tuttavia, non riesce in questo modo: -Perché sudo cat dà un permesso negato ma sudo vim funziona bene?

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 posso apportare modifiche al /etc/pacman.conf manualmente utilizzando vim, facendo

sudo vim /etc/pacman.conf 

e quiting vim con :wq, tutto funziona bene e la mia pacman.conf è stato aggiornato manualmente senza reclami "Autorizzazione negata".

Perché è così? E come posso far funzionare sudo echo? (btw, ho provato a utilizzare anche sudo cat ma non è riuscito con autorizzazione negata)

+3

possibile duplicato di [Come si usa sudo per reindirizzare l'output in un percorso di I don' t avere il permesso di scrivere su?] (http://stackoverflow.com/questions/82256/how-do-i-use-sudo-to-redirect-output-to-a-locazione-i-dont-have-perm ission-to-wr) –

risposta

37

Il problema è che il reindirizzamento viene elaborato dalla shell originale, non da sudo. Le conchiglie non sono in grado di leggere le menti e non sanno che quel particolare >> è pensato per lo sudo e non per questo.

è necessario:

  1. citazione il reindirizzamento (in modo che viene trasmesso ai sudo)
  2. e uso sudo -s (in modo che sudo utilizza un guscio per elaborare il reindirizzamento citato)
.
+1

Così facendo in due passi come questo (1) 'sudo -s' (2) echo" # test ">> /etc/pacman.conf funziona. Ma è possibile eseguirlo in una sola riga? –

+8

'sudo -s 'echo" # test ">>/etc/pacman.conf'' è quello che stavo cercando di trasmettere a voi. – geekosaur

+1

In realtà, l'ho provato dopo aver letto la risposta sopra, ma ho ottenuto uno strano errore come questo: - 'sudo -s 'echo" # test ">> /etc/pacman.conf' /bin/bash: echo" # test " >> /etc/pacman.conf: non esiste un file o una directory di questo tipo, motivo per cui successivamente ho provato il processo manuale in 2 fasi. –

96

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

sudo foo >/some/file 

Il vostro processo shell corrente fa una copia di se stesso che cerca prima di aprire /some/file per la scrittura, poi fa quel file descrittore suo standard di uscita, e solo allora esegue sudo.

Se ti è permesso (file di configurazione sudoer spesso precludono corsa conchiglie), si può fare qualcosa di simile:

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

Ma trovare una buona soluzione, in generale, è quella di utilizzare | sudo tee invece di > e | sudo tee -a invece di >>. Ciò è particolarmente utile se il reindirizzamento è l'unica ragione per cui ho bisogno di sudo in primo luogo; dopo tutto, i processi in esecuzione inutilmente come root sono esattamente ciò che sudo è stato creato per evitare. E l'esecuzione di echo come root è semplicemente sciocca.

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/null alla fine perché tee invia il suo output sia il file denominato e un proprio standard di uscita, e non ho bisogno di vedere sul mio terminale. (Il comando tee si comporta come un connettore "T" in una pipeline fisica, che è dove ottiene il suo nome.) E sono passato a virgolette singole (' ... ') invece di doppio (" ... ") tutto è letterale e non ho dovuto inserire una barra rovesciata davanti allo $ in $arch.

In questo modo è necessario scrivere i file come root utilizzando sudo. Ora per una lunga digressione sui modi per produrre un testo contenente una nuova riga in uno script di shell. :)

In primo luogo, si può solo gruppo tutti i echo 's insieme in una subshell, in modo da avere a che fare solo il reindirizzamento una volta:

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

Oppure utilizzare printf invece di echo, in modo da poter newlines incorporare direttamente nella stringa utilizzando \n:

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

In bash, è possibile ottenere lo stesso risultato con echo -e:

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

Ma la maggior parte delle shell produrrà appena il -e quando si prova, quindi non è raccomandato.

Con entrambi printf e echo -e, quello che il comando si ottengono come una stringa argomento contiene aa backslash seguito da una N letterale ovunque si digita \n, ed è fino al programma di comando stesso (il codice all'interno printf o echo) di tradurre quello in una nuova riga. In molte shell moderne, hai la possibilità di utilizzare le virgolette ANSI $' ... ', che tradurranno sequenze come \n in letterale nuove righe prima che il programma di comando visualizzi mai la stringa, il che significa che tali stringhe funzionano con qualsiasi comando:

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

Ma, mentre più portabile di echo -e, citazioni ANSI sono ancora un'estensione non POSIX.

mio modo preferito per farlo sarebbe quello di utilizzare un qui-document e di evitare la necessità di echo o printf tutto:

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

EOF 
+0

Interessante! Potresti aggiungere una nota che spieghi il motivo per il chunking del file su/dev/null? – knite

+1

@knite Fatto, vedi modifica. –

+0

sudo tee è ottimo per quando usi heredocs. –

15

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

Poiché le istruzioni non sono stato chiaro sopra sono usando le istruzioni da quel post sul blog. Con esempi quindi è più facile vedere cosa devi fare.

$ sudo cat /root/example.txt | gzip> /root/example.gz
-bash: /root/example.gz: Permesso negato

Avviso che è il secondo comando (il comando gzip) nella pipeline che causa l'errore. Ecco dove 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 formano l'uscita del comando ls del fatto che la creazione di file compressi è riuscito.

Il secondo metodo è simile al primo in cui 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

Grazie! I tuoi esempi hanno aiutato! – dharmatech

+1

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

0

FASE 1 creare una funzione in un file bash

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

'EOF' non interpreterà $arch variabile.

STE2 file sorgente bash

$ source write_pacman.sh 

FASE 3 eseguire la funzione

$ write_pacman 
5
sudo bash -c 'echo "[archlinuxfr]" >> /etc/pacman.conf'