Differenza tra cat e '>' per azzerare un file


23

Questi due comandi sono diversi su come vanno azzerare i file? Il secondo è un modo più breve di fare il primo? Cosa sta succedendo dietro le quinte?

Tutti e due

$ cat /dev/null > file.txt

$ > file.txt 

dare la precedenza

-rw-r--r--  1 user  wheel  0 May 18 10:33 file.txt

Risposte:


28

cat /dev/null > file.txtè un uso inutile del gatto .

Fondamentalmente cat /dev/nullsi traduce semplicemente in catnon produrre nulla. Sì, funziona, ma è disapprovato da molti perché provoca l'invocazione di un processo esterno che non è necessario.
È una di quelle cose che è comune semplicemente perché è comune.

L'uso di Just > file.txtfunzionerà sulla maggior parte delle shell, ma non è completamente portatile. Se vuoi essere completamente portatile, le seguenti sono buone alternative:

true > file.txt
: > file.txt

Sia :e trueuscita dati, e sono intrinseche della shell (che catè un programma di utilità esterno), quindi sono più leggeri e più 'corretta'.

 

Aggiornare:

Come ha detto Tylerl nel suo commento, c'è anche la >| file.txtsintassi.

La maggior parte delle shell ha un'impostazione che impedisce loro di troncare un file esistente tramite >. È necessario utilizzare >|invece. Questo serve a prevenire l'errore umano quando intendevi davvero accontentarti >>. È possibile attivare il comportamento con set -C.

Quindi, con questo, penso che il metodo più semplice, più appropriato e portatile per troncare un file sarebbe:

:>| file.txt

2
Il comando due punti è definito in POSIX . È un'operazione nulla esistente per espandere gli argomenti della riga di comando.
Kojiro,

3
LOL, "abuso di gatto"
KM.

2
POSIX ha :anche il mandato di POSIX per essere integrato, e in effetti è diverso dal fatto trueche è considerato un built-in "speciale" .
jw013,

2
non dimenticare di noclobber . >| fileè un troncato più esplicito.
Tylerl,

1
No truenon è richiesto per essere incorporato e tradizionalmente no. :è costruito in tutte le conchiglie della famiglia Bourne. :è un builtin speciale per POSIX (quindi : > fileuscirà dalla shell per esempio se filenon può essere aperto per la scrittura nelle shell POSIX) e truenon lo è. POSIX menziona anche che :potrebbe essere più efficiente rispetto truead alcuni sistemi.
Stéphane Chazelas,

23

In termini di portabilità:

                      Bourne POSIX  zsh    csh/tcsh  rc/es  fish
> file                Y      Y      N(1)   N(1)      N      N
: > file              N/Y(2) Y(3)   Y      Y(4)      N(5)   N(5)
true > file           Y(5)   Y      Y      Y(5)      Y(5)   Y(5)
cat /dev/null > file  Y(5)   Y      Y(5)   Y(5)      Y(5)   Y(5)
eval > file           Y(3,8) Y(3)   Y      Y(6)      Y      Y
cp /dev/null file (7) Y(5)   Y      Y(5)   Y(5)      Y(5)   Y(5)
printf '' > file      Y(5)   Y      Y      Y(5)      Y(5)   Y

Gli appunti:

  1. eccetto in sho kshemulazione, per i reindirizzamenti senza un comando, in zsh, viene assunto un comando predefinito (un cercapersone solo per il reindirizzamento stdin, cataltrimenti), che può essere regolato con le variabili NULLCMD e READNULLCMD. Questo si ispira alla funzionalità simile in(t)csh
  2. Inizialmente i reindirizzamenti non venivano eseguiti :in UnixV7 poiché :venivano interpretati a metà strada tra un commentatore e un comando null. Successivamente sono stati e come per tutti i builtin, se il reindirizzamento fallisce, questo esce dalla shell.
  3. :ed evalessendo built-in speciali, se il reindirizzamento fallisce, esce dalla shell (lo bashfa solo in modalità POSIX).
  4. È interessante notare che in (t)cshquesto sta definendo un'etichetta null (per goto), quindi goto ''lì si ramificherebbe. Se il reindirizzamento non riesce, si esce dalla shell.
  5. A meno / se il comando corrispondente è disponibile in $PATH( :generalmente non è, true, cat, cpe printfgeneralmente sono (POSIX li richiede)).
  6. Se il reindirizzamento non riesce, si esce dalla shell.
  7. Se fileè un collegamento simbolico a un file inesistente, alcune cpimplementazioni come GNU si rifiuteranno di crearlo.
  8. Le versioni iniziali della shell Bourne non supportavano il reindirizzamento dei builtin

In termini di leggibilità:

(questa sezione è altamente soggettiva)

  • > file. Che >sembra troppo simile una richiesta o un commento. Inoltre, la domanda che farò quando la leggerò (e la maggior parte delle shell si lamenterà dello stesso) è quale output stai esattamente reindirizzando? .
  • : > file. :è noto come il comando no-op. In questo modo si legge subito come generare un file vuoto. Tuttavia, anche in questo caso, :può essere facilmente perso e / o visto come un prompt.
  • true > file: cosa c'entra il booleano con il reindirizzamento o il contenuto del file? Cosa si intende qui? è la prima cosa che mi viene in mente quando l'ho letto.
  • cat /dev/null > file. Concatenare /dev/nullin file? catviene spesso visto come il comando per scaricare il contenuto del file, che può ancora dare un senso: il dump del contenuto del il file vuoto infile , un po 'come un modo contorto per dire cp /dev/null file, ma ancora comprensibile.
  • cp /dev/null file. Copia il contenuto del file vuoto in file. Ha senso, anche se qualcuno che non sa come cpfare per impostazione predefinita potrebbe pensare che stai cercando di creare anche fileun nulldispositivo.
  • eval > fileo eval '' > file. Non esegue nulla e reindirizza il suo output a file. Ha senso per me. Strano che non sia un linguaggio comune.
  • printf '' > file: non stampa esplicitamente nulla in un file. Quello che ha più senso per me.

In termini di prestazioni

La differenza sarà se stiamo usando una shell integrata o no. In caso contrario, è necessario eseguire un fork di un processo, il comando deve essere caricato ed eseguito.

evalè garantito per essere costruito in tutte le shell. :è integrato ovunque sia disponibile (Mi piace Bourne / csh). trueè incorporato solo in shell tipo Bourne.

printfè la maggior parte delle conchiglie Bourne e fish.

cpe catgeneralmente non sono integrati.

Ora cp /dev/null filenon invoca reindirizzamenti della shell, quindi cose come:

find . -exec cp /dev/null {} \;

saranno più efficienti di:

find . -exec sh -c '> "$1"' sh {} \;

(anche se non necessariamente di:

find . -exec sh -c 'for f do : > "$f"; done' sh {} +

).

Personalmente

Personalmente, uso : > filein shell tipo Bourne e al giorno d'oggi non uso altro che shell tipo Bourne.


Che dire dd of=file count=0?
Kojiro,

2
@kojiro, Con alcune implementazioni di dd(come almeno Solaris 10), count=0viene ignorato. dd if=/dev/null of=filesarebbe più portatile. In ogni caso, è indipendente dalla shell.
Stéphane Chazelas,

OK, ma non è meno meritevole di inclusione di cp /dev/null file, giusto?
Kojiro,

2
@kojiro, cp /dev/null fileè un linguaggio comune. Sto limitando a quelli, il punto non è elencare tutti i modi possibili.
Stéphane Chazelas,

5

Potresti voler guardare truncate, il che fa esattamente questo: troncare un file.

Per esempio:

truncate --size 0 file.txt

Questo è probabilmente più lento dell'uso true > file.txt.

Il mio punto principale tuttavia è: truncateè destinato al troncamento dei file, mentre l'utilizzo di> ha l'effetto collaterale di troncare un file.


2
Troncare è bello quando vuoi troncare un file su qualcosa diverso da 0. Detto questo, anche senza una shell è una strana affermazione: puoi descrivere un contesto in cui truncatesarebbe disponibile, ma >né le unistdlibrerie C né sarebbero disponibili?
Kojiro,

Non proprio. Probabilmente esiste una soluzione più elegante per ogni script o linguaggio di programmazione disponibile.
Fabian,

3
truncateè un'utilità FreeBSD, relativamente recente (2008) aggiunta ai coreutils GNU (sebbene lo --sizestile di opzione lunga GNU sia specifico GNU), quindi non è disponibile nei sistemi non GNU-o-FreeBSD e non è disponibile nei sistemi GNU precedenti, Non direi che sia portatile. cp /dev/null filefunzionerebbe senza un reindirizzamento della shell e sarebbe più portatile.
Stéphane Chazelas,

Bene, rimuoverò quel commento sulla portabilità. Anche se la tua definizione di recente sembra differire.
Fabian,

2

La risposta dipende un po 'da ciò che file.txtè e da come il processo ci scrive!

Citerò un caso d'uso comune: hai un file di registro in crescita chiamato file.txte vuoi ruotarlo.

Pertanto, ad esempio, si copia file.txtin file.txt.save, quindi si tronca file.txt.

In questo scenario, SE il file non viene aperto da another_process(es: another_processpotrebbe essere un programma in uscita su quel file, ad esempio un programma che registra qualcosa), quindi le tue 2 proposte sono equivalenti ed entrambe funzionano bene (ma la seconda è preferita come prima "cat / dev / null> file.txt" è un uso inutile di Cat e apre e legge anche / dev / null).

Ma il vero problema sarebbe se l' other_processelemento è ancora attivo e ha ancora un handle aperto che va al file.txt.

Quindi, sorgono 2 casi principali, a seconda di come è stato other processaperto il file:

  • Se lo other_processapre in modo normale, l'handle indicherà comunque la posizione precedente nel file, ad esempio con offset 1200 byte. La prossima scrittura inizierà quindi all'offset 1200, e quindi avrai di nuovo un file di 1200bytes (+ qualunque cosa abbia scritto altro_processo), con 1200 caratteri null iniziali! Non è quello che vuoi , presumo.

  • Se other_processaperto file.txtin "modalità append", ogni volta che scrive, il puntatore cercherà attivamente fino alla fine del file. Pertanto, quando lo tronchi, "cercherà" fino al byte 0 e non avrai l'effetto collaterale negativo! Questo è quello che vuoi (... di solito!)

Si noti che ciò significa che, quando si tronca un file, è necessario assicurarsi che tutti quelli other_processche stanno ancora scrivendo in quella posizione lo abbiano aperto in modalità "Aggiungi". Altrimenti dovrai interromperli other_processe riavviarli, in modo che inizino a puntare all'inizio del file anziché nella posizione precedente.

Riferimenti: /programming//a/16720582/1841533 per una spiegazione più chiara e un breve esempio di differenza tra registrazione in modalità normale e aggiunta su /programming//a/984761/1841533


2
Molto poco di questa risposta è in realtà rilevante o risponde alla domanda. La differenza tra a cat /dev/null > filee a > fileè a cat /dev/nulle non fa alcuna differenza per il file.
jw013,

@ jw013: True! Ma volevo solo cogliere l'occasione della domanda per riaffermare le informazioni "cosa vuoi / non cosa vuoi", dato che non sono molto conosciute, e potrei colpire duramente qualcuno che prova a ruotare i registri (un caso comune in cui vuoi tronca un file).
Olivier Dulac,

1
C'è un tempo e un luogo per tutto. Le tue informazioni potrebbero essere utili in qualche altro contesto, ma non appartengono a questo: dovresti trovare un posto più appropriato perché nessuno che cerca di ruotare i registri cercherà in questa domanda di reindirizzamento completamente indipendente. Qui la tua risposta è l'equivalente di un'erbaccia digitale, proprio come una pianta di zucca altrimenti utile nel mezzo di un campo di grano sarebbe considerata un'erbaccia.
jw013,

1

Mi piace e lo uso spesso perché sembra più pulito e non come se qualcuno avesse premuto il tasto Invio per errore:

echo -n "" > file.txt

Dovrebbe essere anche un built-in?


3
Esistono molti modi per azzerare un file. Penso a KM. era interessato solo a capire la differenza tra i due metodi mostrati nella domanda.
drs

6
Molte echoimplementazioni non supportano -n(e verrebbero emesse -n<SPC><NL>qui. printf '' > file.txtSarebbero più portatili (almeno attraverso i sistemi moderni / POSIX).
Stéphane Chazelas,
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.