Il reindirizzamento con `>>` equivale a `>` quando il file di destinazione non esiste ancora?


80

Considera una shell come Bash o sh. La differenza di base tra >e si >>manifesta in un caso in cui esiste il file di destinazione:

  • > tronca il file a dimensione zero, quindi scrive;
  • >> non si tronca, scrive (accoda) alla fine del file.

Se il file non esiste, viene creato con dimensione zero; quindi scritto a. Questo è vero per entrambi gli operatori. Può sembrare che gli operatori siano equivalenti quando il file di destinazione non esiste ancora.

Lo sono davvero?

Risposte:


107

tl; dr

No. >>è essenzialmente "cerca sempre alla fine del file" mentre >mantiene un puntatore all'ultima posizione scritta.


Risposta completa

(Nota: tutti i miei test sono stati eseguiti su Debian GNU / Linux 9).

Un'altra differenza

No, non sono equivalenti. C'è un'altra differenza Può manifestarsi indipendentemente dal fatto che il file di destinazione esistesse prima o meno.

Per osservarlo, eseguire un processo che genera dati e reindirizzare a un file con >o >>(ad es pv -L 10k /dev/urandom > blob.). Lascialo funzionare e modifica le dimensioni del file (ad es. Con truncate). Vedrai che >mantiene il suo offset (crescente) mentre >>si aggiunge sempre alla fine.

  • Se si tronca il file a una dimensione inferiore (può essere zero)
    • >non importa, scriverà all'offset desiderato come se nulla fosse successo; subito dopo il troncamento dell'offset oltre la fine del file, ciò farà sì che il file riacquisti le sue vecchie dimensioni e cresca ulteriormente, i dati mancanti verranno riempiti di zeri (in modo rado, se possibile);
    • >> verrà aggiunto alla nuova estremità, il file crescerà dalla sua dimensione troncata.
  • Se si ingrandisce il file
    • >non importa, scriverà all'offset desiderato come se nulla fosse successo; subito dopo aver modificato la dimensione dell'offset all'interno del file, ciò causerà l'interruzione del file per un po ', fino a quando l'offset non raggiunge la nuova fine, quindi il file crescerà normalmente;
    • >> verrà aggiunto alla nuova estremità, il file crescerà dalla sua dimensione ingrandita.

Un altro esempio è quello di aggiungere (con un separato >>) qualcosa in più quando il processo di generazione dei dati è in esecuzione e sta scrivendo nel file. Questo è simile all'ingrandimento del file.

  • Il processo di generazione con >scriverà all'offset desiderato e alla fine sovrascriverà i dati extra.
  • Il processo di generazione con >>salterà i nuovi dati e li accederà (potrebbero verificarsi condizioni di competizione, i due flussi potrebbero essere interfogliati, tuttavia nessun dato dovrebbe essere sovrascritto).

Esempio

Importa in pratica? C'è questa domanda :

Sto eseguendo un processo che produce molto output su stdout. Invio di tutto a un file [...] Posso usare un qualche tipo di programma di rotazione del registro?

Questa risposta dice che la soluzione è logrotatecon l' copytruncateopzione che si comporta in questo modo:

Tronca il file di registro originale in posizione dopo aver creato una copia, anziché spostare il vecchio file di registro e crearne uno nuovo.

Secondo quanto ho scritto sopra, il reindirizzamento con >renderà grande il registro troncato in pochissimo tempo. La scarsità salverà il giorno, non si dovrà sprecare spazio significativo sul disco. Tuttavia, ogni registro consecutivo avrà sempre più zeri iniziali completamente inutili.

Ma se logrotatecrea copie senza preservare la scarsità, questi zeri iniziali avranno bisogno di sempre più spazio su disco ogni volta che viene fatta una copia. Non ho studiato il comportamento dello strumento, potrebbe essere abbastanza intelligente con scarsità o compressione al volo (se la compressione è abilitata). Tuttavia gli zeri possono solo causare problemi o essere al massimo neutrali; niente di buono in loro.

In questo caso l'utilizzo di >>anziché >è significativamente migliore, anche se il file di destinazione sta per essere ancora creato.


Prestazione

Come possiamo vedere, i due operatori agiscono in modo diverso non solo all'inizio, ma anche in seguito. Ciò può causare qualche differenza di prestazione (sottile?). Per ora non ho risultati di test significativi per supportarlo o confutarlo, ma penso che non dovresti presumere automaticamente che le loro prestazioni siano le stesse in generale.


9
Quindi >>è essenzialmente "cerca sempre alla fine del file" mentre >mantiene un puntatore all'ultima posizione scritta. Sembra che ci potrebbe essere una
leggera

10
A livello di chiamata di sistema, >>utilizza il O_APPENDflag peropen() . E in realtà, >usa O_TRUNC, mentre >>non lo fa. La combinazione di O_TRUNC | O_APPENDsarebbe anche possibile, il linguaggio shell non fornisce proprio quella funzionalità.
ilkkachu,

3
@jjmontes, la fonte standard sarebbe POSIX: pubs.opengroup.org/onlinepubs/9699919799.2018edition/utilities/… ma ovviamente il manuale di Bash ha anche descrizioni degli operatori di reindirizzamento, compresi quelli non standard che supporta: gnu.org/ software / bash / manual / html_node / Redirections.html
ilkkachu

2
@ilkkachu Ho trovato questo per essere di interesse, come spiega i dettagli relativi O_APPEND che mi chiedevo circa dopo il tuo commento :): stackoverflow.com/questions/1154446/...
jjmontes

1
@Mokubai, Qualsiasi sistema operativo sano avrebbe la lunghezza del file a portata di mano quando è aperto, e controllando una bandiera e spostando l'offset alla fine dovrebbe semplicemente scomparire in tutti gli altri libri contabili. Cercare di emulare O_APPENDcon un lseek()prima write()sarebbe diverso, tuttavia ci sarebbe un sovraccarico di chiamata di sistema aggiuntivo. (E ovviamente non funzionerebbe, dal momento che un altro processo potrebbe write()intercorrere tra loro.)
ilkkachu,
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.