Perché dovresti `cat / dev / null> / var / log / messages`?


77

In questa pagina di esempio di scripting bash l'autore presenta questo script:

# Cleanup
# Run as root, of course.

cd /var/log
cat /dev/null > messages
cat /dev/null > wtmp
echo "Log files cleaned up."

Perché dovresti fare cat /dev/nullqualcosa? Non riesco a capire cosa si intende qui (è come usare while TRUE; sleep 1; elihwper {some busy program}?). Eppure l'autore lo chiama "Niente di insolito".

Risposte:


40

In genere, cat /dev/null > [something]quando si desidera cancellare il contenuto del file, garantendo nel contempo che non vi sia alcun rischio zero di interruzione per lo stato del file effettivo. Il contenuto del file sarà chiaramente cancellato dal cat /dev/nullfile stesso, così come esiste e è noto al file system su cui risiede, sarà ancora lì con lo stesso numero di inode, proprietà e permessi.

Nel caso di un file di registro, è possibile che il file di registro stesso sia contrassegnato come "in uso" da un altro processo. Così facendo, ad esempio, un rm /var/log/messages && touch /var/log/messagessarebbe dannoso per altri processi e potrebbe provocare il soffocamento dei processi in esecuzione. Significa che un processo che in qualche modo è bloccato su uno specifico numero di inode collegato al file /var/log/messagespotrebbe improvvisamente farsi prendere dal panico e dire: “Ehi! Che cosa è successo /var/log/messages! ”Anche se il file è ancora lì. Per non parlare dei potenziali problemi con la proprietà e le autorizzazioni che vengono ricreate in modo errato.

A causa di questa incertezza nell'uso / stato di un file, l'uso di cat /dev/null > [something]è preferito dagli amministratori di sistema che vogliono cancellare un registro ma non vogliono interferire potenzialmente con il funzionamento di processi già esistenti.

Inoltre, nel contesto della pagina che si collega all'autore si afferma quanto segue:

Non c'è nulla di insolito qui, solo un insieme di comandi che avrebbero potuto essere invocati altrettanto facilmente uno alla volta dalla riga di comando sulla console o in una finestra del terminale. I vantaggi di inserire i comandi in uno script vanno ben oltre il fatto di non doverli digitare nuovamente di volta in volta.

Quindi il "nulla di insolito" menzionato dall'autore riguarda l'intero concetto di ciò che è quello specifico script bash: è solo un insieme di semplici comandi che possono essere eseguiti altrettanto facilmente dalla riga di comando ma sono inseriti in un file di testo per evitare di doverli riscrivere più e più volte.


10
@jlliagre L'unica cosa che viene "mantenuta in vita" è il contesto della domanda che si concentra sul perché l'autore originale lo farebbe. Se sei appassionato di dissipare il "mito", pubblica una risposta che fornisca un contesto per il metodo di codifica dell'autore originale, nonché una prospettiva sul perché ritieni che possa essere sostituito da altri metodi.
Jake Gould,

7
@jlliagre La risposta di Cyrus non affronta la domanda principale sul perché l'autore originale avrebbe usato quel metodo né il ragionamento alla base. Il tutorial menzionato è piuttosto vecchio e ragionevolmente accettato. Non è errato né perpetua una presunta "leggenda urbana". Piuttosto è il modo in cui una persona codifica rispetto al modo in cui un'altra persona codifica. Così semplice. È un problema di stile che non ha alcun impatto negativo su affidabilità o prestazioni.
Jake Gould,

3
truncate -s 0farebbe la stessa cosa e sarebbe meno idiomatico. Tuttavia, i programmatori di shell sono un gruppo conservatore e si potrebbero incontrare sistemi abbastanza vecchi o abbastanza stravaganti da mancare quel comando.
Schwern,

5
@skift cat /dev/null > /foo/bartronca il file; echo "" > /foo/barlo tronca e quindi scrive un singolo carattere di nuova riga.
David,

2
Un altro vantaggio di questo metodo rispetto a rm & touch è che la proprietà e le autorizzazioni sono mantenute.
jjmontes,

121

Perché dovresti cat / dev / null su qualcosa?

Lo farai per troncare il contenuto di un file mantenendo intatto l'inode. Tutti i programmi che hanno quel file aperto per la lettura o la scrittura non sarebbero interessati al di fuori del fatto che la dimensione del file sarebbe ripristinata a zero.

Un'alternativa fasulla che si trova spesso è rimuovere il file e poi crearlo nuovamente:

rm file
touch file

o simili:

mv file file.old
gzip file.old
touch file

Il problema è che questi metodi non impediscono che il vecchio file venga mantenuto da qualsiasi processo che abbia il file eliminato aperto al momento della cancellazione. Il motivo per cui è nei file system Unix, quando si elimina un file, si scollega solo il suo nome (percorso) dal suo contenuto (inode). L'inode viene mantenuto in vita finché ci sono processi che lo aprono per la lettura o la scrittura.

Ciò comporta numerosi effetti negativi: i registri scritti dopo l'eliminazione del file vengono persi in quanto non esiste un modo semplice / portatile per aprire un file eliminato. Finché un processo sta scrivendo sul file eliminato, il suo contenuto sta ancora utilizzando lo spazio sul file system. Ciò significa che se si rimuove / crea il file perché stava riempiendo il disco, il disco rimane pieno. Un modo per risolvere quest'ultimo problema è riavviare i processi del logger, ma potresti non voler farlo per i servizi critici e i log degli intermediari andrebbero definitivamente persi. Ci sono anche effetti collaterali a causa del fatto che il file che crei potrebbe non avere gli stessi permessi, proprietario e gruppo di quello originale. Ciò, ad esempio, potrebbe impedire a un analizzatore di log di leggere il file appena creato o, peggio ancora, impedire al processo di registrazione di scrivere i propri log.

Il primo metodo, cat /dev/null > fileraggiungere l'obiettivo correttamente , tuttavia, nonostante una tenace leggenda urbana, la sua cat /dev/nullparte non fa assolutamente nulla di utile. Apre uno pseudo file che è vuoto di progettazione, non riesce a leggere nulla da esso e alla fine esce. L'uso di questo comando è quindi uno spreco di sequenze di tasti, byte, chiamate di sistema e cicli della CPU e può essere sostituito senza alcuna modifica funzionale dal comando no-op indubbiamente più veloce: o addirittura, con la maggior parte delle shell, da nessun comando.

Vorrei provare una metafora per spiegare quanto sia inutile cat /dev/null. Diciamo che il tuo obiettivo è quello di svuotare un bicchiere.

  • Prima rimuovi qualsiasi liquido da esso. Questo è sufficiente ed è esattamente ciò che ( > file) dato il fatto che i reindirizzamenti vengono sempre elaborati per primi.

  • Quindi, scegli una bottiglia vuota ( /dev/null) e la versi nel bicchiere vuoto ( cat). Questo è il passo inutile ...

Se leggi il documento collegato fino alla fine, potresti notare i commenti in questa riga dalla versione avanzata dello script:

    cat / dev / null> wtmp #   ':> wtmp' e '> wtmp' hanno lo stesso effetto.

Hanno davvero; troppo male è cat /dev/nullstato mantenuto nel codice.

Ciò significa che il seguente codice funzionerà con tutte le shell comuni (sia cshe shfamiglie):

cd /var/log
: > messages
: > wtmp
echo "Log files cleaned up."

e questo lavorerà con tutte le shell utilizzando la sintassi Bourne, come ash, bash, ksh, zshe simili:

cd /var/log
> messages
> wtmp
echo "Log files cleaned up."

Si noti tuttavia che con antiche shell Bourne pre-POSIX, uno qualsiasi di questi comandi, incluso cat /dev/null non troncerà un file se viene scritto successivamente da uno script di shell ancora in esecuzione che lo aggiunge. Invece di un file a zero byte, sarebbe un file sparso con le sue dimensioni invariate. Lo stesso accadrebbe se il file è stato scritto da un processo che cerca la posizione che ritiene sia quella corrente prima di scrivere.

Attenzione anche che alcune soluzioni alternative suggeriscono spesso di troncare un file presentano dei difetti.

  • Entrambi i seguenti semplicemente non fanno il lavoro. Il file risultante non è vuoto ma contiene una riga vuota. Ciò spezzerebbe i file di registro come quelli wtmpche archiviano record a larghezza fissa.

    echo > file
    echo "" > file
  • Il prossimo basato su shun'opzione BSD non è portatile, POSIX non specifica alcuna opzione consentita per l'eco, quindi potresti finire con un file contenente una riga con " -n":

    echo -n > file
  • Quello non portatile neanche usando una shsequenza di fuga di System V. Alcune shell creeranno un file contenente una riga con " \c":

    echo "\c" > file
  • Quello usa un comando progettato per fare il lavoro. Il problema che sta utilizzando truncatenon è portatile perché questo comando, non specificato da POSIX, potrebbe non essere presente in un sistema Unix / Linux.

    truncate -s 0

Infine, ecco un paio di alternative che sono portatili e faranno correttamente il lavoro:

  • Stampa esplicita di una stringa vuota nel file:

    printf "" > file
  • Usando il truecomando che è strettamente equivalente a quello non operativo :sebbene più leggibile:

    true > file

1
@JonathanLeffler Credo che faccia parte di tutte le cshimplementazioni attuali sebbene non sia necessariamente documentato. Dalla csh pagina del manuale di Solaris : Null command. This command is interpreted, but performs no action.. Non ho trovato alcuna menzione della stessa nella tcshpagina di manuale o in quelle originali di BSD csh, ma questa sintassi potrebbe aver sempre funzionato.
jlliagre,

6
Una ragione per scrivere cat /dev/nullè rendere esplicite le tue intenzioni.
Davidmh,

1
@Davidmh Sarebbe sensato ma la tua affermazione non resiste ai controlli sui fatti. Ho osservato questo idioma da shell probabilmente per un paio di decenni. Quando ho avuto l'opportunità di chiedere all'autore il ragionamento alla base, ho sempre avuto teorie errate sull'utilizzo /dev/nulldell'essere un modo efficiente per iniettare zero byte, comunque più affidabile dell'uso :o niente. In questa stessa pagina, la risposta di Cyrus che era concisa ma diretta al punto aveva zero voti mentre quella di JakeGould che stava sfidando il fatto cat /dev/nullera una no-op (vedi i nostri commenti) aveva già almeno quattro voti.
jlliagre,

2
@jlliagre la mia conoscenza della shell è piuttosto semplice, quindi forse non sono il miglior target di popolazione; ma un nudo >o :non è chiaro. Sono d'accordo che è un peccato che i miti siano stati propagati a causa del suo uso.
Davidmh,

4
@Davidmh Se vuoi davvero qualcosa di esplicito prima del reindirizzamento, consiglierei printf "" > fileche è sia portatile (POSIX) che leggero, spesso implementato come shell incorporato.
jlliagre,

6

È un modo ingombrante per portare il file a dimensione zero.

cd /var/log
> messages
> wtmp
echo "Log files cleaned up."

Questa sintassi non funzionerà in ogni shell.
reinierpost,

2
Corretta. La domanda è taggata solo con "bash".
Ciro,

@reinierpost Funzionerà con tutte le shell in stile Bourne, no? Non preoccupiamoci csh.
Barmar,

: > messagesfunziona anche. :o truesono le scelte più ovvie per i comandi che non stampano nulla e restituiscono true.
Peter Cordes,

-3

Per troncare un file aperto. Questo è equivalente e più comprensibile:

echo -n > /var/log/messages

(Aggiunto -n per evitare la nuova riga)


3
Questo è davvero più comprensibile ma purtroppo non equivalente. Romperà persino le funzionalità come nel wtmpcaso. Vedi la mia risposta aggiornata.
jlliagre,

echo -n evita newline.
bbaassssiiee,

6
In effetti, se si è sicuri di usare bash, -naltrimenti non è garantito il funzionamento in tutte le shell Bourne.
jlliagre,
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.