Come posso usare sudo per reindirizzare l'output in una posizione in cui non ho il permesso di scrivere?


881

Mi è stato concesso l'accesso sudo su una delle nostre scatole Linux RedHat di sviluppo e mi sembra di trovarmi abbastanza spesso a dover reindirizzare l'output in una posizione a cui normalmente non ho accesso in scrittura.

Il problema è che questo esempio inventato non funziona:

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

Ho appena ricevuto la risposta:

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

Come posso farlo funzionare?


1
usa chmod u + w nomefile
Szabolcs Dombi

@DombiSzabolcs Mi stai suggerendo di creare prima il file come sudo, quindi concedermi l'autorizzazione? Buona idea.
Jonathan,

In molte situazioni finisci qui perché hai chiesto "perché mi viene negata l'autorizzazione?" A volte la risposta è che devi creare un file come root(nel qual caso procedi a leggere le risposte) ma molto spesso, devi semplicemente creare il file altrove, come te stesso.
tripla il

1
Dopo aver lottato con queste risposte, ho finalmente scelto di reindirizzare a un file temporaneo e sudo spostarlo a destinazione.
TingQian LI,

Risposte:


1233

Il comando non funziona perché il reindirizzamento viene eseguito dalla shell che non dispone dell'autorizzazione per scrivere /root/test.out. Il reindirizzamento dell'output non viene eseguito da sudo.

Esistono più soluzioni:

  • Esegui una shell con sudo e assegnagli il comando usando l' -copzione:

    sudo sh -c 'ls -hal /root/ > /root/test.out'
    
  • Crea uno script con i tuoi comandi ed eseguilo con sudo:

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

    Corri sudo ls.sh. Vedi la risposta di Steve Bennett se non vuoi creare un file temporaneo.

  • Avviare una shell con sudo -squindi eseguire i comandi:

    [nobody@so]$ sudo -s
    [root@so]# ls -hal /root/ > /root/test.out
    [root@so]# ^D
    [nobody@so]$
    
  • Usa sudo tee(se devi scappare molto quando usi l' -copzione):

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

    Il reindirizzamento a /dev/nullè necessario per impedire che il t venga emesso sullo schermo. Per aggiungere invece di sovrascrivere il file di output ( >>), utilizzare tee -ao tee --append(l'ultimo è specifico per i coreutils GNU ).

Grazie a Jd , Adam J. Forster e Johnathan per la seconda, terza e quarta soluzione.


1
C'è una grande risposta che ti dice come reindirizzare STDERR e STDOUT separatamente qui: stackoverflow.com/questions/692000/... ... in fondoperl -e 'print "STDIN\n"; print STDERR "STDERR\n"; ' > >( tee stdout.log ) 2> >( tee stderr.log >&2 )
errant.info

2
Ti consigliamo di fare 'sudo -E ...' per usare le variabili nel comando sgusciato (quando lo usi in uno script, per esempio).
Urhixidur,

3
Il reindirizzamento dell'uscita tee su / dev / null non è probabilmente necessario in molti casi in cui l'eco dell'output sullo schermo è innocuo. Ad esempio, quando si tratta solo dell'output di comandi regolari o del contenuto di piccoli file di testo.
thomasrutter,

105

Qualcuno qui ha appena suggerito di fare una t-shirt:

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

Questo potrebbe anche essere usato per reindirizzare qualsiasi comando a una directory a cui non si ha accesso. Funziona perché il programma tee è effettivamente un programma "eco a un file", e il reindirizzamento a / dev / null serve a fermarlo anche emettendo sullo schermo per mantenerlo lo stesso dell'esempio originale inventato sopra.


5
In molti casi, vale a dire se l'utente normale ha i permessi per eseguire il comando e "solo" non può scrivere nel file di output desiderato, il primo sudo(cioè per il comando stesso) potrebbe essere omesso
Hagen von Eitzen,

81

Un trucco che ho capito da solo è stato

sudo ls -hal /root/ | sudo dd of=/root/test.out

16
sudo ddè migliore degli sudo tee /root/file > /dev/nullesempi sopra!
kristianlm,

14
dd non è uno strano comando oscuro. Viene utilizzato ogni volta che è necessario copiare grandi quantità di dati con buffering tra due dispositivi a blocchi. La sintassi è in realtà piuttosto semplice, ddè il nome del comando, of=/root/test.outè l'argomento che dice ddcosa sono i file di output.
rhlee

14
@steve Tutto è "oscuro" fino a quando non impari cos'è. ofsignifica file di output ed ddè uno strumento molto popolare utilizzato sia in Linux che in OSX (principalmente per scrivere immagini su dischi). Questo è sicuramente un trucco.
Blockloop

12
ddè probabilmente ugualmente utile come teequi. In entrambi i casi stai usando un comando comune e ben noto per uno scopo che, sebbene leggermente diverso dal suo scopo originale previsto, è ancora ben noto e ben documentato. Mentre ddè bravo a copiare enormi quantità di dati, non fa schifo con piccole quantità. Qui ha il vantaggio di non fare eco anche all'output standard.
thomasrutter,

26
Ogni volta che si digita le parole sudo dduna a fianco all'altra, si vuole fare molto, molto sicuro che gli argomenti che seguono sono corrette (soprattutto se si considera la sua sintassi non standard). Non lo chiamano "distruttore di dischi" per niente ...
ali_m

45

Il problema è che il comando viene eseguito in sudo, ma il reindirizzamento viene eseguito sotto l'utente. Questo viene fatto dalla shell e c'è molto poco che puoi fare al riguardo.

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

I soliti modi per aggirare questo sono:

  • Avvolgi i comandi in uno script che chiami sotto sudo.

    Se i comandi e / o il file di registro cambiano, puoi fare in modo che lo script li prenda come argomenti. Per esempio:

    sudo log_script command /log/file.txt
    
  • Chiamare una shell e passare la riga di comando come parametro con -c

    Ciò è particolarmente utile per i comandi composti una tantum. Per esempio:

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

23

Ancora un'altra variazione sul tema:

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

O ovviamente:

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

Hanno il (piccolo) vantaggio di non dover ricordare alcun argomento sudoo sh/bash


18

Chiarire un po 'perché è preferibile l'opzione tee

Supponendo che tu abbia l'autorizzazione appropriata per eseguire il comando che crea l'output, se installi l'output del tuo comando su Tee, devi solo elevare i privilegi di Tee con sudo e T diretto per scrivere (o aggiungere) al file in questione.

nell'esempio fornito nella domanda significherebbe:

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

per un paio di esempi più pratici:

# 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

In ciascuno di questi esempi stai prendendo l'output di un comando non privilegiato e scrivendo in un file che di solito è scrivibile solo da root, che è l'origine della tua domanda.

È una buona idea farlo in questo modo perché il comando che genera l'output non viene eseguito con privilegi elevati. Non sembra importare qui, echoma quando il comando source è uno script di cui non ti fidi completamente, è cruciale.

Nota che puoi usare l'opzione -a per aggiungere T (append >>) al file target piuttosto che sovrascriverlo (like >).


Siamo spiacenti js3, ma questo è già stato suggerito (nel 2008) e si trova alla seconda risposta più alta: stackoverflow.com/a/82553/6910
Jonathan

2
Hai ragione, Jonathan, aggiornerò la mia risposta per espandere i motivi per cui questa è un'opzione preferibile. Grazie per il feedback utile.
jg3

17

Fai eseguire a sudo una shell, in questo modo:

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

11

Il modo in cui vorrei andare su questo problema è:

Se è necessario scrivere / sostituire il file:

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

Se è necessario aggiungere al file:

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

1
In che modo ciò è sostanzialmente diverso dalle risposte sopra?
Jonathan,

2
Non è "sostanzialmente diverso" ma chiarisce l'uso distintivo tra la sostituzione e l'aggiunta al file.
Jamadagni,

1
Questa è la risposta che ho copiato sul mio cheat sheet.
MortimerCat

5

Che ne dici di scrivere una sceneggiatura?

Nome file: myscript

#!/bin/sh

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

# end script

Quindi utilizzare sudo per eseguire lo script:

sudo ./myscript

4

Ogni volta che devo fare qualcosa del genere divento semplicemente root:

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

Probabilmente non è il modo migliore, ma funziona.


4

Lo farei così:

sudo su -c 'ls -hal /root/ > /root/test.out'

2
In realtà sembra un po 'più pulito, poiché non è necessario specificare esplicitamente la shell.
Steve Bennett,

1
Un piccolo inconveniente è che esegue un ulteriore processo ( 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

Non intendo battere un cavallo morto, ma ci sono troppe risposte qui che usano tee, il che significa che devi reindirizzare stdouta /dev/nullmeno che tu non voglia vedere una copia sullo schermo.

Una soluzione più semplice è usare catcosì:

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

Notare come il reindirizzamento viene inserito tra virgolette in modo che venga valutato da una shell avviata sudoanziché da quella che lo esegue.


Questo funziona bene. Non capisco perché abbia avuto un voto negativo. Upvoted.
Teemu Leisti,

4
Penso che non ottenga molto amore perché non è molto meglio di sudo bash -c "ls -hal /root > /root/test.out". L'uso del tee evita di aver bisogno di un guscio, mentre il gatto no.
Nick Russo,

2

Questo si basa sulla risposta che coinvolge tee. Per rendere le cose più facili ho scritto un piccolo script (lo chiamo suwrite) e l'ho inserito /usr/local/bin/con il +xpermesso:

#! /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

Come mostrato in USAGE nel codice, tutto ciò che devi fare è reindirizzare l'output a questo script seguito dal nome file accessibile al superutente desiderato e ti chiederà automaticamente la tua password se necessario (poiché include sudo).

echo test | suwrite /root/test.txt

Si noti che poiché si tratta di un semplice wrapper per tee, accetterà anche l' -aopzione di tee da aggiungere e supporta anche la scrittura su più file contemporaneamente.

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

Ha anche una protezione semplicistica contro la scrittura su /dev/dispositivi che era una preoccupazione menzionata in uno dei commenti su questa pagina.


1

Forse ti è stato concesso l'accesso sudo solo ad alcuni programmi / percorsi? Quindi non c'è modo di fare quello che vuoi. (a meno che non lo modifichi in qualche modo)

In caso contrario, forse puoi scrivere lo script bash:

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

Premi ctrl+ d:

chmod a+x myscript.sh
sudo myscript.sh

Spero che sia d'aiuto.


1
sudo at now  
at> echo test > /tmp/test.out  
at> <EOT>  
job 1 at Thu Sep 21 10:49:00 2017  

1
Se hai sudocomunque, atnon è utile o necessario. sudo sh -c 'echo test >/tmp/test.out'fa lo stesso in modo molto più efficiente ed elegante (ma soffre ancora del difetto che probabilmente stai eseguendo cose rootche non richiedono quel privilegio; dovresti generalmente evitare comandi privilegiati quando puoi).
Tripleee
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.