È sicuro reindirizzare stdout e stderr allo stesso file senza copie del descrittore di file?


27

Comincio in una directory vuota.

$ touch aFile
$ ls
aFile

Quindi ho lsdue argomenti, uno dei quali non è in questa directory. Reindirizzo entrambi i flussi di output su un file denominato output. Uso >>per evitare di scrivere contemporaneamente.

$ ls aFile not_exist >>output 2>>output
$ cat output
ls: cannot access 'not_exist': No such file or directory
aFile

Che sembra funzionare. Ci sono pericoli in questo approccio?


6
È stato un voto veloce. Ci sono voluti circa cinque secondi. Puoi dirmi come è possibile valutare la dignità della mia domanda così rapidamente? E ancora meglio, cosa c'è che non va in modo da poterlo migliorare?
exit_status,

Perché non usi più standard ls aFile not_exist &>>outputqui? (Nota, presumo che tu stia usando bash .)
FedonKadifeli,

5
Perché questo non mi aiuta a capire di cosa sto chiedendo. So come reindirizzare questi flussi sullo stesso file, anche in modo portabile. Quello che voglio sapere è se c'è qualcosa di sbagliato in ciò che ho suggerito nella domanda. @FedonKadifeli
exit_status

1
@FedonKadifeli &>>NON è standard. È una sintassi DEPRECATA e ambigua che funziona in modo diverso nelle diverse shell. Mi chiedo da dove prendiate le vostre cose.
Zio Billy,

4
Bash non è uno standard . I mandati standard POSIX che ls &>>foo ...dovrebbero essere analizzati come due comandi ls &e >>foo ..., in questo modo, altre shell come /bin/shUbuntu lo stanno analizzando. Poiché è deprecato, puoi guardare qui , anche se non pretendo che sia un tipo di autorità. bashTuttavia, potresti chiedere ai manutentori se ritengono di usarla.
Zio Billy,

Risposte:


22

No, non è solo sicuro come lo standard >>bar 2>&1.

Quando scrivi

foo >>bar 2>>bar

stai aprendo il barfile due volte con O_APPEND, creando due oggetti file completamente indipendenti [1], ognuno con il proprio stato (puntatore, modalità di apertura, ecc.).

Questo è molto diverso da quello 2>&1che sta semplicemente chiamando la dup(2)chiamata di sistema e rende gli alias intercambiabili stderr e stdout per lo stesso oggetto file.

Ora, c'è un problema con questo:

O_APPENDpuò portare a file corrotti su filesystem NFS se più di un processo aggiunge dati a un file contemporaneamente. Questo perché NFS non supporta l'aggiunta di un file, quindi il kernel del client deve simularlo, cosa che non può essere fatta senza una race condition.

Di solito è possibile contare sulla probabilità del file, come barin foo >>bar 2>&1essere scritto allo stesso tempo da due luoghi separati che sono piuttosto bassi. Ma dal tuo l' >>bar 2>>barhai appena aumentato di una dozzina di ordini di grandezza, senza alcun motivo.

[1] "Apri descrizioni dei file" nel linguaggio POSIX.


3
Formalmente, per i file in modalità append, è sicuro . Il problema citato è un bug in NFS che lo rende inadatto (non conforme a POSIX) come file system. Per il caso in modalità non append, tuttavia, non è mai sicuro.
R ..

1
È irrilevante. La doppia appendice del PO non è sicura da usare (oltre ad essere completamente inutile). Ed O_APPENDè comunque una specie di pasticcio: abbastanza oneroso da implementare correttamente.
mosvy

Credo che la condizione di gara NFS sia solo tra client diversi. Il sistema operativo client deve coordinare tutte le scritture tra i suoi processi.
Barmar,

@Barmar sarebbe vero se al sistema operativo client importasse solo la propria vista di un file nfs. Ma quando si scrive nel file nfs aperto con O_APPEND, il client recupererà prima la dimensione "reale" del file dal server ("riconvalida" l'inode) e quindi eseguirà l'aggiornamento inode cerca + scrivi + in cache, e solo l'ultima parte è fatto sotto lock, il che significa che la prima parte potrebbe ancora recuperare una dimensione obsoleta dal server e sovrascrivere quella corretta dall'inode locale / memorizzato nella cache. Lo stesso problema con lseek(SEEK_END).
mosvy

Ancora non vedo come ciò possa causare condizioni di gara tra due flussi sullo stesso client. Entrambi i flussi dovrebbero fare riferimento allo stesso inode locale memorizzato nella cache.
Barmar

22

Cosa succede quando lo fai

some_command >>file 2>>file

è che fileverrà aperto per l'aggiunta di due volte. Questo è sicuro da fare su un filesystem POSIX. Qualsiasi scrittura che si verifica sul file quando viene aperta per l'aggiunta verrà eseguita alla fine del file, indipendentemente dal fatto che i dati provengano dal flusso di output standard o dal flusso di errori standard.

Questo si basa sul supporto per le operazioni di scrittura atomica dell'appendice nel filesystem sottostante. Alcuni filesystem, come NFS, non supportano l'appendice atomica. Vedi ad es. La domanda "Il file append è atomico in UNIX?" Su StackOverflow.

utilizzando

some_command >>file 2>&1

funzionerebbe anche su NFS però.

Tuttavia, usando

some_command >file 2>file

non è sicuro, poiché la shell troncherà il file di output (due volte) e qualsiasi scrittura che accadrà su entrambi i flussi sovrascriverà i dati già scritti dall'altro flusso.

Esempio:

$ { echo hello; echo abc >&2; } >file 2>file
$ cat file
abc
o

La hellostringa viene prima scritta (con una nuova riga che termina), quindi la stringa abcseguita da una nuova riga viene scritta dall'errore standard, sovrascrivendo il hell. Il risultato è la stringa abccon una nuova riga, seguita da ciò che resta del primo echooutput, una oe una nuova riga.

Scambiare i due echoattorno alla ferita produce solo hellonel file di output poiché quella stringa è scritta per ultima ed è più lunga della abcstringa. L'ordine in cui si verificano i reindirizzamenti non ha importanza.

Sarebbe meglio e più sicuro usare il più idiomatico

some_command >file 2>&1

1
Sebbene ciò sia vero per le shell moderne, non era il caso della shell Bourne o Thomson (da dove >>viene), dove si >>sarebbe aperta per la scrittura e avrebbe cercato fino alla fine (suppongo perché O_APPEND non era ancora stato inventato ancora allora). Anche su Solaris 10, /bin/sh -c '(echo a; echo b >&2) >> file 2>> file; cat file'uscite b.
Stéphane Chazelas

@ StéphaneChazelas È un problema con l'implementazione di Solaris 10 sho con il suo filesystem?
Kusalananda

1
Questo è quello che >>stava facendo originariamente, non si stava aprendo con O_APPEND, si stava aprendo senza e cercando fino alla fine. Non è un grosso problema, è ciò che stava facendo e che è stato documentato.
Stéphane Chazelas,

0

Dipende da cosa vuoi ottenere. Sta a te decidere se va bene avere errori nello stesso file dell'output. Questo è solo il salvataggio del testo in un file con la funzionalità della shell che ti consente di reindirizzare come desideri. Non esiste un sì o un assoluto. Come in Linux, tutto può essere fatto in diversi modi, questo è il mio modo di ls notExistingFile existingFile >> output 2>&1 rispondere alla domanda: in termini di reindirizzamento, sì, è perfettamente sicuro.


C'è di più oltre a quello che stai dicendo qui. Lo stesso esercizio con >invece di >>sovrascriverà alcuni personaggi. Quindi non è solo che la shell mi consente di reindirizzare, perché quando reindirizzo con >, il risultato è diverso. Quindi ci sono sfumature con >, ce ne sono con >>?
exit_status,

Sì, sarà diverso. Come ho detto, dipende dal tuo obiettivo >: sovrascrivere. >>- append
Angel
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.