Reindirizzamento di stdout su un file per il quale non si dispone dell'autorizzazione di scrittura


113

Quando si tenta di modificare un file senza disporre delle autorizzazioni di scrittura, viene visualizzato un errore:

> touch /tmp/foo && sudo chown root /tmp/foo
> echo test > /tmp/foo
zsh: permission denied: /tmp/foo

Il Sudoing non aiuta, perché esegue il comando come root, ma la shell gestisce il reindirizzamento di stdout e apre comunque il file come te:

> sudo echo test > /tmp/foo
zsh: permission denied: /tmp/foo

C'è un modo semplice per reindirizzare stdout su un file su cui non hai il permesso di scrivere, oltre ad aprire una shell come root e manipolare il file in quel modo?

> sudo su
# echo test > /tmp/foo

2
Risposta per una domanda simile da StackOverflow stackoverflow.com/questions/82256/...
Cristian Ciupitu

come puoi non avere il permesso per un file che hai creato tu stesso in tmp? è perché di umask?
k961,

@ k961 Ho usato chownper cambiare il proprietario; era solo un esempio
Michael Mrozek

Risposte:


116

Sì, usando tee. Così echo test > /tmp/foodiventa

echo test | sudo tee /tmp/foo

Puoi anche aggiungere ( >>)

echo test | sudo tee -a /tmp/foo

27
Tee verrà anche emesso su stdout; a volte non vuoi che il contenuto riempia lo schermo. Per risolvere questo problema, faiecho test | sudo tee /tmp/foo > /dev/null
Shawn J. Goff il

Come lo farai con Heredoc?
JohnDoea,

26

Per sostituire il contenuto del file con l'output di echo(come l' >operatore di reindirizzamento della shell).

echo test | sudo dd of=/tmp/foo

Per scrivere nel file (all'inizio, anche se è possibile utilizzare seekper l'output in offset diversi) senza troncare (come l' 1<>operatore shell Bourne):

echo test | sudo dd of=/tmp/foo conv=notrunc

Per aggiungere al file (come >>), con GNU dd:

echo test | sudo dd of=/tmp/foo oflag=append conv=notrunc

Vedere anche GNU dd's conv=exclper evitare di sovrascrivere un file esistente (come con set -o noclobberin shell POSIX) e conv=nocreatper il contrario (solo aggiornare un file esistente).


intelligente! questo allevia la necessità di fare echo test | sudo tee /tmp/foo >/dev/nullper scartare l'output.
Adam Katz,

2
Potrei doverlo riprendere; ddnon è affidabile per questo a meno che tu non stia usando oscure opzioni solo per GNU iflag=fullblock oflag=fullblock, che rimuovono l'eleganza di questa risposta. Continuerò con tee.
Adam Katz,

5
dd è affidabile con il non oscuro bs = 1
umeboshi

2
@umeboshi Ma affidabile solo se hai abbastanza esperienza per sapere esattamente cosa stai facendo. Perché ddpuò essere abbastanza pericoloso (se non per dire: devastante) se si commettesse solo un leggero errore. Quindi, per i nuovi utenti, preferirei raccomandare il teemetodo per essere al sicuro.
syntaxerror,

1
@AdamKatz, nel caso di dd of=filesolo (senza count/ skip...), è affidabile. iflag=fullblocknon è necessario perché qui ddscrive sull'output ciò che ha letto sull'input. Non importa se non fosse blocchi completi.
Stéphane Chazelas,

12

tee è probabilmente la scelta migliore, ma a seconda della situazione potrebbe essere sufficiente qualcosa del genere:

sudo sh -c 'echo test > /tmp/foo'

1
3 problemi: evalinvece di un semplice sh -c; -i, che cambierà la tua directory di lavoro; eseguendo l'intero comando come root, che potrebbe cambiarne il comportamento o introdurre un rischio non necessario. perché questo ha mai ottenuto un voto?

4
non l'hai fatto, ma è fuorviante, generalmente cattivo e forse persino pericoloso.

5

Mentre sono d'accordo, questo | sudo teeè il modo canonico, a volte sed (qui assumendo GNU sed) può funzionare:

cat sudotest 
line 1

sudo sed -i '1iitest' sudotest && cat sudotest 
itest
line 1

sudo sed -i '$aatest' sudotest && cat sudotest 
itest
line 1
atest

-imodifica il file in atto. 1isignifica inserire prima della riga 1. $asignifica aggiungere dopo l'ultima riga.

Oppure copia su xclipboard:

somecommand | xclip
sudo gedit sudotest
move cursor to desired place, click middle mouse button to insert, save

1
Questo è formulato come un indovinello cinese antico. Non riesco a capire come questo abbia ottenuto così tanti voti. ( hrmph )
syntaxerror

1
@syntaxerror: Signore, mi dispiace, non sono un madrelingua inglese. Se specifichi ciò che non ti è chiaro, potrei provare a migliorare la mia risposta. Rispetto ai 65 voti positivi di Gert, neanche 4 sembrano essere tanti voti positivi, non credi?
utente sconosciuto

Beh, sarei abbastanza soddisfatto di 4 :-D Il tuo inglese non è affatto male. È appena scritto in una specie di nerd techie terse. Devo indovinare che sei un programmatore. I programmatori tra loro si capiranno perfettamente in quel modo, ma un non programmatore deve pensare che sia formulato come ... come detto.
syntaxerror

Si noti che in sed -irealtà non modifica il file in atto : crea un file temporaneo e lo rinomina all'uscita. Quindi non sarai in grado di fare qualcosa di simile tail -f ...sul file originale e vedere l'output usando sed -i ...mentre la pipeline è in esecuzione
Andrew Henle

@AndrewHenle: Sì, dal momento che le dimensioni possono essere aumentate o ridotte, e poiché questo è probabilmente il caso della maggior parte delle invocazioni sed, e non puoi nemmeno - afaik - scrivere nella stessa posizione su SSD è solo un'operazione pseudo 'sul posto' . Come madrelingua inglese, posso chiedere una breve espressione, che non è così probabilmente mal interpretata? Solo -i creates a new file of same nameo c'è qualcosa di più compatto? Immagino che mi piaccia in place, perché spiega il i. La manpage gnu-sed lo chiama anche al suo posto e la bandiera lunga è --in-place.
utente sconosciuto

2

Ho preso a calci nella parte posteriore della mia mente idee per un problema simile e ho trovato le seguenti soluzioni:

  • sudo uncatdove si uncattrova un programma che legge l'input standard e lo scrive nel file indicato sulla riga di comando, ma non ho ancora scritto uncat.

  • sudocatla variante sudoeditche non ho ancora scritto che fa un detergente sudo cato sudo uncat.

  • o questo piccolo trucco da usare sudoeditcon un EDITOR che è uno script di shell

    #!/bin/sh
    # uncat
    cat > "$1"
    

    che può essere invocato come uno |sudo ./uncat fileo | EDITOR=./uncat sudoeditma con interessanti effetti collaterali.


catprende un elenco di file di con gatto e spo, quindi UNCAT dovrebbe prendere un elenco di file a un con gatto e spo a. Dovrebbe usare la magia per decidere quanto mettere in ogni file. Nome alternativo includono dog, to-file, redirect.
ctrl-alt-delor,

Non riesco a pensare a nessun motivo per cui vorrei uncatquando ho tee.
Wildcard il

1
Bene, teeha il banale inconveniente che scrive il suo stdin sul suo stdout - che è banalmente mitigato dal reindirizzamento dello stdout /dev/null. Altre alternative includono dd of=/tmp/foo(menzionate in un'altra risposta), che scrive le informazioni sullo stato su stderr e cp /dev/stdin /tmp/foo.
Scott,

1

Usa la spugna dal pacchetto moreutils. Ha il vantaggio di non scrivere su stdout.

echo test | sudo sponge /tmp/foo

Utilizzare l'opzione -a per aggiungere a un file invece di sovrascriverlo.


1
Non sono sicuro di quale vantaggio non scrivere a stdout presenta, ma potresti semplicemente reindirizzare l'output /dev/nullse fosse un problema
Michael Mrozek

1
Ovviamente posso reindirizzare a / dev / null, ma il comando è più facile da leggere e digitare senza il reindirizzamento. Il vantaggio di non scrivere su stdout è che il mio terminale non è pieno di immondizia.
Hontvári Levente,

1
Non installerei un pacchetto per risparmiare un reindirizzamento, ma uso regolarmente la spugna, quindi è già lì.
Hontvári Levente,

0

L'errore deriva dall'ordine in cui la shell fa le cose.

Il reindirizzamento viene gestito prima ancora che la shell sudovenga eseguita e quindi viene eseguita con le autorizzazioni dell'utente su cui si sta attualmente lavorando. Poiché non si dispone delle autorizzazioni di scrittura per creare / troncare la destinazione del reindirizzamento, viene visualizzato un permission deniederrore dalla shell.

La soluzione è garantire che il file di output venga creato con l'identità fornita da sudo, ad esempio con tee:

$ generate_output | sudo tee target_file
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.