> & - è più efficiente di> / dev / null?


58

Ieri ho letto questo commento SO che dice che nella shell (almeno bash) >&-"ha lo stesso risultato di" >/dev/null.

Tale commento in realtà si riferisce alla guida ABS come la fonte delle sue informazioni. Ma quella fonte dice che la >&-sintassi "chiude i descrittori di file".

Non mi è chiaro se le due azioni di chiusura di un descrittore di file e reindirizzamento sul dispositivo null siano totalmente equivalenti. Quindi la mia domanda è: sono?

In apparenza sembra che chiudere un descrittore sia come chiudere una porta ma reindirizzarlo a un dispositivo nullo sta aprendo una porta al limbo! I due non mi sembrano esattamente gli stessi perché se vedo una porta chiusa, non proverò a buttar via niente da essa, ma se vedo una porta aperta, suppongo di poterlo fare.

In altre parole, mi sono sempre chiesto se >/dev/nullsignifica che cat mybigfile >/dev/nullavrebbe effettivamente elaborato ogni byte del file e scriverlo nel /dev/nullquale lo dimentica. D'altra parte, se la shell incontra un descrittore di file chiuso, tendo a pensare (ma non sono sicuro) che semplicemente non scriverà nulla, anche se rimane la domanda se leggeràcat comunque ogni byte.

Questo commento dice >&-e >/dev/null" dovrebbe " essere lo stesso, ma non è una risposta così clamorosa per me. Mi piacerebbe avere una risposta più autorevole con qualche riferimento a standard o core di base o no ...


Se volessi essere benefico, direi che il commento non significava che fossero stati implementati allo stesso modo, solo che entrambi avevano lo stesso risultato finale di impedirti di vedere l'output del programma.
Barmar,

Risposte:


71

No, sicuramente non vuoi chiudere i descrittori di file 0, 1 e 2.

Se lo fai, la prima volta che l'applicazione apre un file, diventerà stdin / stdout / stderr ...

Ad esempio, se lo fai:

echo text | tee file >&-

Quando tee(almeno alcune implementazioni, come busybox ') apre il file per la scrittura, sarà aperto sul descrittore di file 1 (stdout). Quindi teescriverò textdue volte in file:

$ echo text | strace tee file >&-
[...]
open("file", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 1
read(0, "text\n", 8193)                 = 5
write(1, "text\n", 5)                   = 5
write(1, "text\n", 5)                   = 5
read(0, "", 8193)                       = 0
exit_group(0)                           = ?

Questo è noto per causare vulnerabilità alla sicurezza. Per esempio:

chsh 2>&-

E chsh(un'applicazione setuid) potrebbe finire per scrivere messaggi di errore in /etc/passwd.

Alcuni strumenti e persino alcune biblioteche cercano di proteggerlo. Ad esempio, GNU teesposterà il descrittore di file su uno sopra 2 se ai file che apre per la scrittura vengono assegnati 0, 1, 2, a differenza di busybox tee.

La maggior parte degli strumenti, se non riescono a scrivere su stdout (perché ad esempio non è aperto), segnalerà un messaggio di errore su stderr (nella lingua dell'utente che significa elaborazione extra per aprire e analizzare i file di localizzazione ...), quindi sarà significativamente meno efficiente e probabilmente causerà il fallimento del programma.

In ogni caso, non sarà più efficiente. Il programma eseguirà comunque una write()chiamata di sistema. Può essere più efficiente solo se il programma smette di scrivere su stdout / stderr dopo la prima write()chiamata di sistema non riuscita , ma i programmi generalmente non lo fanno. Generalmente escono con un errore o continuano a provare.


4
Penso che questa risposta sarebbe ancora migliore se l'ultimo paragrafo fosse in cima (poiché è ciò che risponde più direttamente alla domanda del PO), e poi ha continuato a discutere sul perché sia ​​una cattiva idea anche se ha funzionato principalmente. Ma lo prendo, ho un voto. ;)
un CVn

@ StéphaneChazelas: Come ha detto Michael, mi sarei aspettato l'ultimo para in cima, ma grazie per averlo chiarito probabilmente probabilmente crea problemi. Quindi suppongo che sarebbe una domanda accessoria, quando sarà davvero utile chiudere un FD? O dovrei fare una domanda separata?
Jamadagni,


1
@jamadagni Se il link fornito da Stéphane non risponde alla domanda, direi che sembra l'inizio di una domanda separata in quanto non è direttamente correlato all'efficienza relativa dei due metodi.
un CVn

1
Apprezzo che Stephane inizi con questo vert importante avviso di sicurezza, poiché sarebbe meno visibile se l'ultimo paragrafo fosse in cima. +1 da me.
Olivier Dulac,

14

IOW Mi sono sempre chiesto se >/dev/nullsignifica che cat mybigfile >/dev/nullavrebbe effettivamente elaborato ogni byte del file e scriverlo nel /dev/nullquale lo dimentica.

Non è una risposta completa alla tua domanda, ma sì, quanto sopra è come funziona.

catlegge i file nominati o l'input standard se non sono stati nominati file e restituisce al loro output standard il contenuto di questi fino a quando non incontra un EOF sull'ultimo file indicato (incluso l'input standard). Questo è il suo lavoro.

Aggiungendo >/dev/nullsi reindirizza l'output standard su / dev / null. Questo è un file speciale (un nodo del dispositivo) che elimina qualsiasi cosa scritta in esso (e restituisce immediatamente EOF in lettura). Si noti che il reindirizzamento I / O è una funzionalità fornita dalla shell, non da ogni singola applicazione, e che non c'è nulla di magico nel nome / dev / null, solo ciò che accade lì sulla maggior parte dei sistemi simili a Unix .

È anche importante notare che la meccanica specifica dei nodi del dispositivo varia da sistema operativo a sistema operativo, ma cat (che, in un sistema GNU, significa coreutils) è multipiattaforma (lo stesso codice sorgente deve essere eseguito almeno su Linux e Hurd) e quindi non può assumere dipendenze da specifici kernel del sistema operativo. Inoltre, funziona ancora se si crea un alias / dev / null (su Linux, questo significa un nodo del dispositivo con lo stesso numero di dispositivo maggiore / minore) con un altro nome. E c'è sempre il caso di scrivere da qualche altra parte che si comporti effettivamente allo stesso modo (diciamo, / dev / zero).

Ne consegue che catnon è a conoscenza delle proprietà speciali di / dev / null, e in effetti probabilmente non è a conoscenza del reindirizzamento in primo luogo, ma deve comunque eseguire esattamente lo stesso lavoro: legge i file nominati e genera il contenuto di il / quei file (s) al suo output standard. Che l'output standard di catcapiti di andare nel vuoto non è qualcosa in catsé.


2
Per estendere la tua risposta: Sì, cat mybigfile > /dev/nullfarà catleggere ogni byte di bigfilein memoria. E, per ogni nbyte, chiamerà write(1, buffer, n). All'insaputa del catprogramma, la writevolontà non farà assolutamente nulla (tranne forse per qualche banale contabilità). La scrittura su /dev/nullnon richiede l'elaborazione di ogni byte.
G-Man dice "Ripristina Monica" il

2
Ricordo di essere rimasto senza parole quando ho letto il sorgente del kernel Linux del dispositivo / dev / null. Mi aspettavo che ci fosse un elaborato sistema di buffer gratuiti (), ecc., Ma, no, è sostanzialmente un return ().
Brian Minton,

4
@ G-Man: non so che puoi garantire che sia vero in tutti i casi. Non riesco a trovare prove ora, ma ricordo qualche implementazione di uno dei due cato cpche funzionerebbe mmapingingendo grossi pezzi del file sorgente in memoria, quindi chiamando write()la regione mappata. Se stavi scrivendo /dev/null, la write()chiamata ritornerebbe immediatamente senza errori nelle pagine del file sorgente, quindi non verrebbe mai effettivamente letta dal disco.
Nate Eldredge,

2
Inoltre, qualcosa come GNU catfunziona su molte piattaforme, ma uno sguardo casuale al codice sorgente mostrerà molti #ifdefs: non è letteralmente lo stesso codice che gira su tutte le piattaforme e ci sono molte sezioni dipendenti dal sistema.
Nate Eldredge,

@NateEldredge: punto interessante, ma stavo solo basando sulla risposta di Michael, quindi non mi stai contraddicendo tanto quanto stai contraddicendo Michael.
G-Man dice "Ripristina Monica" il
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.