Perché il reindirizzamento dell'output di un file su se stesso produce un file vuoto?


19

Perché il reindirizzamento dell'output di un file su se stesso produce un file vuoto?

Dichiarato a Bash, perché farlo

less foo.txt > foo.txt

e

fold foo.txt > foo.txt

produrre un vuoto foo.txt? Poiché un'appendice come less eggs.py >> eggs.pyproduce due copie del testo eggs.py, ci si potrebbe aspettare che una sovrascrittura produca una copia del testo.

Nota, non sto dicendo che si tratta di un bug, è probabilmente un puntatore a qualcosa di profondo su Unix.


Risposte:


20

Quando si utilizza >, il file viene aperto in modalità troncamento in modo che il suo contenuto venga rimosso prima che il comando tenti di leggerlo.

Quando si utilizza >>, il file viene aperto in modalità append in modo da conservare i dati esistenti. In questo caso è comunque piuttosto rischioso utilizzare lo stesso file di input e output. Se il file è abbastanza grande da non adattarsi alla dimensione del buffer di input di lettura, la sua dimensione potrebbe aumentare indefinitamente fino a quando il file system è pieno (o viene raggiunta la quota del disco).

Se si desidera utilizzare un file sia come input che come output con un comando che non supporta la modifica in atto, è possibile utilizzare un paio di soluzioni alternative:

  • Utilizzare un file intermedio e sovrascrivere quello originale al termine e solo se non si è verificato alcun errore durante l'esecuzione dell'utilità (questo è il modo più sicuro e più comune).

    fold foo.txt > fold.txt.$$ && mv fold.txt.$$ foo.txt
  • Evitare il file intermediario a spese di una potenziale perdita di dati parziale o completa in caso di errore o interruzione. In questo esempio, i contenuti di foo.txtvengono passati come input a una subshell (tra parentesi) prima che il file venga eliminato. L'inode precedente rimane attivo mentre la subshell la tiene aperta durante la lettura dei dati. Il file scritto dall'utilità interna (qui fold) pur avendo lo stesso nome (foo.txt) punta a un diverso inode perché la vecchia voce della directory è stata rimossa in modo tecnico, ci sono due "file" diversi con lo stesso nome durante il processo. Al termine della subshell, il vecchio inode viene rilasciato e i suoi dati vengono persi. Fai attenzione a assicurarti di avere abbastanza spazio per archiviare temporaneamente sia il vecchio file che quello nuovo allo stesso tempo, altrimenti perderai i dati.

    (rm foo.txt; fold > foo.txt) < foo.txt

3
spongeda moreutils può anche aiutare. fold foo.txt | sponge foo.txt- o fold foo.txt | sponge !$dovrebbe anche fare.
Slhck,

@slhck In effetti, anche la spugna potrebbe fare il lavoro. Tuttavia, non essendo né specificato da POSIX né mainstream in Unix come i sistemi operativi, è improbabile che sia presente.
jlliagre,

Non è come se non potesse essere reso presente;)
slhck

7

Il file viene aperto per la scrittura dalla shell prima che l'applicazione abbia la possibilità di leggerlo. L'apertura del file per la scrittura lo tronca.


0

In bash, l'operatore di reindirizzamento del flusso si ... > foo.txtsvuota foo.txt prima di valutare l'operando di sinistra .

Si potrebbe usare la sostituzione dei comandi e stamparne il risultato come soluzione alternativa. Questa soluzione richiede meno caratteri aggiuntivi rispetto ad altre risposte:

printf "%s\n" "$(less foo.txt)" > foo.txt

Attenzione: questo comando non preserva alcuna nuova riga in entrata foo.txt. Dai un'occhiata nella sezione commenti qui sotto per maggiori informazioni

Qui, la subshell $(...)viene valutata prima dell'operatore di reindirizzamento dello stream >, quindi la conservazione delle informazioni.


@KamilMaciorowski: In realtà, c'è tmp=$(cmd; printf q);  printf '%s' "${tmp%q}". Ma hai perso un altro problema con questa risposta: dice "subshell" quando significa "sostituzione di comando". Sì, le sostituzioni di comandi sono generalmente subshells, ma non viceversa, e le subshells, in generale, non sono di aiuto per questo problema.
Scott,

@KamilMaciorowski Mi sento così male per aver perso tutto questo. Grazie per aver indicato tutto questo. Per il tuo (4) punto: i backquotes farebbero il trucco, vale a dire preservare i newline finali?
Louis-Jacob Lebel,

@Scott grazie per la tua risposta. Ho cambiato "subshell" per "sostituzione comando". A proposito, mi chiedo qual è la differenza esatta tra i due.
Louis-Jacob Lebel,

No, anche i backquotes (backtick) rimuovono i caratteri di nuova riga.
Kamil Maciorowski

Bene allora, ho aggiunto un messaggio di avvertimento per ora. Lo rimuoverò se trovo una soluzione.
Louis-Jacob Lebel,
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.