Probabilmente intendi "Autorizzazione negata", che è ciò che find
Ubuntu ti mostra quando non puoi accedere a qualcosa a causa delle autorizzazioni dei file, piuttosto che "accesso negato".
Un comando completamente generale che lo fa correttamente (e, come bonus, è portatile per altri * nix es, purché il messaggio di errore sia lo stesso) è:
(find 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-
(Di solito si desidera passare alcuni argomenti a find
. Questi vanno prima del primo reindirizzamento 3>&1
.)
Tuttavia, spesso sarai in grado di utilizzare qualcosa di più semplice. Ad esempio, probabilmente è possibile utilizzare la sostituzione del processo . Seguono i dettagli.
I metodi più comuni e i loro limiti
I due approcci tipici sono di eliminare stderr (come nella risposta di Zanna ) o reindirizzare stderr a stdout e filtrare stdout (come nella risposta di Dev Android ). Sebbene abbiano il vantaggio di essere semplici da scrivere e spesso sono scelte ragionevoli, questi approcci non sono ideali.
Eliminare tutto ciò che viene inviato a stderr, ad esempio reindirizzandolo al dispositivo null con 2>/dev/null
o chiudendolo con, 2>&-
comporta il rischio di errori mancanti diversi da "Autorizzazione negata".
"Autorizzazione negata" è probabilmente l'errore più comune riscontrato durante l'esecuzione find
, ma è tutt'altro che l'unico errore possibile e, se si verifica un altro, è consigliabile conoscerlo. In particolare, find
segnala "Nessun file o directory" se non esiste un punto di partenza. Con più punti di partenza, find
può comunque restituire alcuni risultati utili e sembrare funzionare. Per esempio, se a
e c
esistere, ma b
non lo fa, find a b c -name x
stampe risultati in a
, poi "No such file or directory" per b
, poi i risultati a c
.
Combinando stdout e stderr insieme in stdout e eseguendo il piping su grep
o su qualche altro comando per filtrarlo, come con 2>&1 | grep ...
o, si |& grep ...
corre il rischio di filtrare involontariamente un file il cui nome contiene il messaggio filtrato.
Ad esempio, se si filtrano le righe che contengono "Autorizzazione negata", verranno eliminati anche i risultati della ricerca che mostrano nomi di file come "Autorizzazione negata messaggi.txt". Ciò probabilmente accadrà per caso, anche se sarebbe anche possibile che un file riceva un nome appositamente predisposto per contrastare le tue ricerche.
Il filtraggio dei flussi combinati ha un altro problema, che non può essere mitigato filtrando in modo più selettivo (ad esempio con grep -vx 'find: .*: Permission denied'
sul lato destro del tubo). Alcune find
azioni, inclusa l' -print
azione implicita quando non si specifica alcuna azione, determinano come produrre i nomi dei file in base al fatto che stdout sia o meno un terminale.
- Se non è un terminale, i nomi dei file vengono emessi così come sono anche se contengono strani caratteri come newline e caratteri di controllo che potrebbero cambiare il comportamento del tuo terminale. Se si tratta di un terminale, questi caratteri vengono soppressi e
?
vengono invece stampati.
- Questo è di solito quello che vuoi. Se hai intenzione di elaborare ulteriormente i nomi dei file, devono essere stampati letteralmente. Tuttavia, se si desidera visualizzarli, un nome file con una nuova riga potrebbe altrimenti imitare più nomi di file e un nome file con una sequenza di caratteri backspace potrebbe apparire con un nome diverso. Sono anche possibili altri problemi, come i nomi di file contenenti sequenze di escape che cambiano i colori nel terminale.
- Ma il piping dei risultati della ricerca attraverso un altro comando (come
grep
) find
non fa più vedere un terminale. (Più precisamente, fa sì che il suo stdout non sia un terminale.) Quindi vengono emessi letteralmente strani caratteri. Ma se tutto il comando sul lato destro della pipe è (a) rimuovere le righe che sembrano messaggi "Autorizzazione negata" e (b) stampare ciò che rimane, allora sei ancora soggetto al tipo di shenanigans che find
è il terminale il rilevamento ha lo scopo di prevenire.
- Vedere la sezione FILENAMI INUSUALI di
man find
per ulteriori informazioni, incluso il comportamento di ciascuna delle azioni che stampano i nomi dei file. ( "Molte delle azioni di seguito troviamo nella stampa dei dati, che è sotto il controllo di altri utenti ..." ) vedi anche sezioni 3.3.2.1 , 3.3.2.2 e 3.3.2.3 del manuale di riferimento GNU Findutils .
La discussione sopra di nomi di file insoliti riguarda GNU find , che è l' find
implementazione in sistemi GNU / Linux incluso Ubuntu.
Lasciare da solo l'output standard durante il filtraggio dell'errore standard
Quello che veramente vuole qui è quello di lasciare stdout intatta mentre tubazioni stderr a grep
. Sfortunatamente non esiste una sintassi semplice per questo. |
pipe stdout e alcune shell (incluso bash
) supportano il |&
pipe di entrambi i flussi, oppure puoi reindirizzare prima stderr su stdout 2>&1 |
, il che ha lo stesso effetto. Ma le shell comunemente usate non forniscono solo una sintassi per pipe stderr.
Puoi ancora farlo. È solo imbarazzante. Un modo è scambiare stdout con stderr , in modo che i risultati della ricerca siano su stderr e gli errori siano su stdout, quindi reindirizzare stdout a grep
per filtrare:
find args 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied'
Di solito passerai argomenti a find
, come punti di partenza (i luoghi da cui cercare, che di solito sono directory) e predicati (test e azioni). Questi vanno al posto di args
sopra.
Funziona introducendo un nuovo descrittore di file da conservare su uno dei due flussi standard che si desidera scambiare, eseguendo reindirizzamenti per scambiarli e chiudendo il nuovo descrittore di file.
- Il descrittore di file 1 è stdout e 2 è stderr (e lo 0 non reindirizzato è stdin ). Ma puoi anche reindirizzare utilizzando altri descrittori di file. Questo può essere usato per aprire o mantenere aperto un file o dispositivo.
3>&1
reindirizza il descrittore di file 3 su stdout, in modo che quando lo stdout (descrittore di file 1) viene successivamente reindirizzato, lo stdout originale può ancora essere scritto facilmente.
1>&2
reindirizza stdout a stderr. Poiché il descrittore di file 3 è ancora lo stdout originale, è ancora possibile accedervi.
2>&3
reindirizza stderr al descrittore di file 3, che è lo stdout originale.
3>&-
chiude il descrittore di file 3, che non è più necessario.
- Per ulteriori informazioni, vedere Come eseguire il pipe di stderr e non di stdout? e reindirizzamento IO - Scambiare stdout e stderr (Avanzato) e soprattutto reindirizzare solo stderr attraverso un filtro .
Tuttavia, questo metodo ha lo svantaggio che i risultati della ricerca vengono inviati a stderr e gli errori vengono inviati a stdout . Se stai eseguendo questo comando direttamente in una shell interattiva e non esegui il piping o il reindirizzamento dell'output, non importa. Altrimenti, potrebbe essere un problema. Se metti quel comando in uno script e poi qualcuno (forse tu, in seguito) reindirizza o reindirizza il suo output, non si comporta come previsto .
La soluzione è di scambiare i flussi dopo aver finito di filtrare l'output . L'applicazione degli stessi reindirizzamenti mostrati sopra sul lato destro della pipeline non raggiungerà questo obiettivo, perché |
solo i tubi stdout, quindi quel lato della pipeline riceve solo l'output originariamente inviato a stderr (perché i flussi sono stati scambiati) e non l'originale uscita stdout. Invece, puoi usare (
)
per eseguire il comando sopra in una sottostruttura ( correlata ), quindi applicare i reindirizzamenti di scambio a quello:
(find args 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-
È il raggruppamento, non specificamente la subshell, che fa funzionare tutto ciò. Se preferisci, puoi usare {
;}
:
{ find args 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied'; } 3>&1 1>&2 2>&3 3>&-
Un modo meno ingombrante: sostituzione del processo
Alcune shell, incluso Bash su sistemi in grado di supportarlo (inclusi sistemi GNU / Linux come Ubuntu), consentono di eseguire la sostituzione di processo , che consente di eseguire un comando e reindirizzare verso / da uno dei suoi flussi. È possibile reindirizzare lo find
stderr del comando su un grep
comando che lo filtra e reindirizzare grep
lo stdout di quel comando su stderr.
find args 2> >(grep -Fv 'Permission denied' >&2)
Il merito va a Android Dev per questa idea.
Sebbene bash
supporti la sostituzione dei processi, sh
in Ubuntu lo è dash
. Se si tenta di utilizzare questo metodo, verrà visualizzato "Errore di sintassi: reindirizzamento imprevisto", mentre il metodo di scambio di stdout e stderr continuerà a funzionare. Inoltre, quando bash
viene eseguito in modalità POSIX , il supporto per la sostituzione del processo è disattivato.
Una situazione in cui bash
viene eseguita in modalità POSIX è quando viene invocato come sh
1 . Pertanto, su un sistema operativo come Fedora dove bash
fornisce /bin/sh
, o se il /bin/sh
collegamento simbolico è stato puntato bash
su Ubuntu, la sostituzione del processo non funziona ancora in uno sh
script, senza un comando precedente per disattivare la modalità POSIX. La tua scommessa migliore, se vuoi usare questo metodo in uno script, è quella di metterti #!/bin/bash
in cima invece che #!/bin/sh
, se non lo sei già.
1 : in questa situazione, bash
attiva automaticamente la modalità POSIX dopo aver eseguito i comandi nei suoi script di avvio.
Un esempio
È utile poter testare questi comandi. Per fare ciò, creo una tmp
sottodirectory della directory corrente e la popolo con alcuni file e directory, togliendo le autorizzazioni da una di esse per attivare un errore "Autorizzazione negata" find
.
mkdir tmp; cd tmp; mkdir a b c; touch w a/x 'a/Permission denied messages.txt' b/y c/z; chmod 0 b
Una delle directory che è accessibile include un file con "Permesso negato" nel suo nome. L'esecuzione find
senza reindirizzamenti o pipe mostra questo file, ma mostra anche l'errore "Autorizzazione negata" effettiva per un'altra directory non accessibile:
ek@Io:~/tmp$ find
.
./a
./a/Permission denied messages.txt
./a/x
./c
./c/z
./w
./b
find: ‘./b’: Permission denied
Piping sia su stdout che su stderr grep
e filtrando le righe che contengono "Autorizzazione negata" fa sparire il messaggio di errore ma nasconde anche il risultato della ricerca per il file con quella frase nel suo nome:
ek@Io:~/tmp$ find |& grep -Fv 'Permission denied'
.
./a
./a/x
./c
./c/z
./w
./b
find 2>&1 | grep -Fv 'Permission denied'
è equivalente e produce lo stesso output.
I metodi sopra indicati per filtrare "Autorizzazione negata" solo dai messaggi di errore e non dai risultati della ricerca hanno esito positivo. Ad esempio, ecco il metodo in cui vengono scambiati stdout e stderr:
ek@Io:~/tmp$ (find 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-
.
./a
./a/Permission denied messages.txt
./a/x
./c
./c/z
./w
./b
find args 2> >(grep -Fv 'Permission denied' >&2)
produce lo stesso output.
È possibile attivare un messaggio di errore diverso per assicurarsi che le righe inviate a stderr che non contengono il testo "Autorizzazione negata" siano ancora consentite. Ad esempio, qui ho eseguito find
con la directory corrente ( .
) come punto di partenza, ma la directory inesistente foo
come un altro:
ek@Io:~/tmp$ (find . foo 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-
.
./a
./a/Permission denied messages.txt
./a/x
./c
./c/z
./w
./b
find: ‘foo’: No such file or directory
Verificare che l find
'output standard sia ancora un terminale
Possiamo anche vedere quali comandi causano la visualizzazione letterale di caratteri speciali, come le nuove righe. (Questo può essere fatto separatamente dalla dimostrazione sopra e non deve essere nella tmp
directory.)
Crea un file con una nuova riga nel suo nome:
touch $'abc\ndef'
Di solito usiamo le directory come punti di partenza per find
, ma anche i file funzionano:
$ find abc*
abc?def
Il piping di stdout a un altro comando provoca l'output letterale della nuova riga, creando la falsa impressione di due risultati di ricerca separati abc
e def
. Possiamo provarlo con cat
:
$ find abc* | cat
abc
def
Il reindirizzamento di solo stderr non causa questo problema:
$ find abc* 2>/dev/null
abc?def
Né chiuderlo:
$ find abc* 2>&-
abc?def
Tubazioni per grep
fa causa il problema:
$ find abc* |& grep -Fv 'Permission denied'
abc
def
(La sostituzione |&
con 2>&1 |
è equivalente e produce lo stesso output.)
Scambiare stdout e stderr e piping stdout non causa il problema find
: lo stdout diventa stderr, che non viene convogliato:
$ find abc* 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied'
abc?def
Raggruppare quel comando e scambiare nuovamente i flussi non causa il problema:
$ (find abc* 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-
abc?def
(La {
;}
versione produce lo stesso output.)
L'uso della sostituzione di processo per filtrare stderr non causa neanche il problema:
$ find abc* 2> >(grep -Fv 'Permission denied' >&2)
abc?def