Qual è la differenza tra "du -sh *" e "du -sh ./*"?


Risposte:


69
$ touch ./-c $ 'a \ n12 \ tb' pippo
$ du -hs *
0 a
12 b
0 pippo
0 totale

Come puoi vedere, il -cfile è stato preso come opzione per due non è riportato (e vedi la totallinea a causa di du -c). Inoltre, il file chiamato ci a\n12\tbsta facendo pensare che ci siano file chiamati ae b.

$ du -hs -- *
0       a
12      b
0       -c
0       foo

Va meglio. Almeno questo tempo -cnon è considerato un'opzione.

$ du -hs ./*
0       ./a
12      b
0       ./-c
0       ./foo

È anche meglio. Il ./prefisso impedisce -cdi essere preso come opzione e l'assenza di ./prima bnell'output indica che non ci sono bfile lì dentro, ma c'è un file con un carattere di nuova riga (ma vedi sotto 1 per ulteriori digressioni su quello).

È buona norma utilizzare il ./prefisso quando possibile e, in caso contrario, per dati arbitrari, è necessario utilizzare sempre :

cmd -- "$var"

o:

cmd -- $patterns

Se cmdnon supporta il --segno della fine delle opzioni, dovresti segnalarlo come un bug al suo autore (tranne quando è per scelta e documentato come per echo).

Ci sono casi in cui ./*risolve problemi che --non lo fanno. Per esempio:

awk -f file.awk -- *

fallisce se c'è un file chiamato a=b.txtnella directory corrente (imposta la variabile awk asu b.txtinvece di dirgli di elaborare il file).

awk -f file.awk ./*

Non ha il problema perché ./anon è un nome di variabile awk valido, quindi ./a=b.txtnon viene preso come assegnazione di variabile.

cat -- * | wc -l

fallisce se esiste un file chiamato -nella directory corrente, poiché dice catdi leggere dal suo stdin ( -è speciale per la maggior parte delle utility di elaborazione del testo e per cd/ pushd).

cat ./* | wc -l

è OK perché ./-non è speciale per cat.

Cose come:

grep -l -- foo *.txt | wc -l

per contare il numero di file che contengono foosono errati perché presuppone che i nomi dei file non contengano caratteri di nuova riga ( wc -lconta i caratteri di nuova riga, quelli generati da grepciascun file e quelli nei nomi dei file stessi). Dovresti usare invece:

grep -l foo ./*.txt | grep -c /

(il conteggio del numero di /caratteri è più affidabile in quanto può essercene solo uno per nome file).

Per ricorsivo grep, il trucco equivalente è usare:

grep -rl foo .//. | grep -c //

./* può avere alcuni effetti collaterali indesiderati però.

cat ./*

aggiunge altri due caratteri per file, in modo da raggiungere prima il limite della dimensione massima degli argomenti + ambiente. E a volte non vuoi che ./sia riportato nell'output. Piace:

grep foo ./*

Uscita:

./a.txt: foobar

invece di:

a.txt: foobar

Altre digressioni

1 . Mi sento come se dovessi approfondire qui, dopo la discussione nei commenti.

$ du -hs ./*
0       ./a
12      b
0       ./-c
0       ./foo

Sopra, che ./segnare l'inizio di ogni file significa che possiamo identificare chiaramente dove ogni nome di file inizia (a ./) e dove finisce (alla nuova riga prima della successiva ./o della fine dell'output).

Ciò significa che l'output di du ./*, contrariamente a quello di du -- *) può essere analizzato in modo affidabile, anche se non così facilmente in uno script.

Quando l'output arriva a un terminale, ci sono molti altri modi in cui un nome file può ingannare:

  • Controlla i personaggi, le sequenze di escape possono influenzare il modo in cui le cose vengono visualizzate. Ad esempio, \rsposta il cursore all'inizio della riga, \bsposta il cursore indietro, \e[Cavanti (nella maggior parte dei terminali) ...
  • molti personaggi sono invisibili su un terminale a partire da quello più ovvio: il personaggio spaziale.
  • Ci sono personaggi Unicode che hanno lo stesso aspetto della barra nella maggior parte dei caratteri

    $ printf '\u002f \u2044 \u2215 \u2571 \u29F8\n'
    /    

    (vedi come va nel tuo browser).

Un esempio:

$ touch x 'x ' $'y\bx' $'x\n0\t.\u2215x' $'y\r0\t.\e[Cx'
$ ln x y
$ du -hs ./*
0       ./x
0       ./x
0       ./x
0       .∕x
0       ./x
0       ./x

Molti xma ymancano.

Alcuni strumenti come GNUls sostituiscono i caratteri non stampabili con un punto interrogativo (si noti che (U + 2215) è stampabile) quando l'output va su un terminale. GNU duno.

Ci sono modi per farli rivelare:

$ ls
x  x   x?0?.∕x  y  y?0?.?[Cx  y?x
$ LC_ALL=C ls
x  x?0?.???x  x   y  y?x  y?0?.?[Cx

Guarda come ci siamo rivolti ???dopo che abbiamo detto lsche il nostro set di caratteri era ASCII.

$ du -hs ./* | LC_ALL=C sed -n l
0\t./x$
0\t./x $
0\t./x$
0\t.\342\210\225x$
0\t./y\r0\t.\033[Cx$
0\t./y\bx$

$segna la fine della riga, in modo che possiamo individuare il "x"vs "x ", tutti i caratteri non stampabili e i caratteri non ASCII sono rappresentati da una sequenza di barre rovesciate (la barra stessa sarebbe rappresentata con due barre rovesciate), il che significa che non è ambiguo. Quello era GNU sed, dovrebbe essere lo stesso in tutte le sedimplementazioni conformi a POSIX ma nota che alcune vecchie sedimplementazioni non sono altrettanto utili.

$ du -hs ./* | cat -vte
0^I./x$
0^I./x $
0^I./x$
0^I.M-bM-^HM-^Ux$

(non standard ma abbastanza comune, anche cat -Acon alcune implementazioni). Quello è utile e usa una rappresentazione diversa ma è ambiguo ( "^I"e <TAB>sono visualizzati uguali per esempio).

$ du -hs ./* | od -vtc
0000000   0  \t   .   /   x  \n   0  \t   .   /   x      \n   0  \t   .
0000020   /   x  \n   0  \t   . 342 210 225   x  \n   0  \t   .   /   y
0000040  \r   0  \t   . 033   [   C   x  \n   0  \t   .   /   y  \b   x
0000060  \n
0000061

Quello è standard e inequivocabile (e coerente da un'implementazione all'altra) ma non è così facile da leggere.

Noterai che ynon è mai apparso sopra. Questo è un problema completamente non correlato a du -hs *quello che non ha nulla a che fare con i nomi dei file, ma va notato: poiché dusegnala l'utilizzo del disco, non segnala altri collegamenti a un file già elencato (non tutte le duimplementazioni si comportano in questo modo anche se quando sono elencati i collegamenti reali sulla riga di comando).


+1, bello e completo (per quanto posso dire ^^). In particolare, adoro il vantaggio "grep -c /". Vale anche la pena notare: il vantaggio di "./*" rispetto a "*" appare in una delle (molte) buone risposte alle FAQ di Unix (probabilmente su faqs.org. Iirc, è nella domanda sui file rm-ing che iniziano con un "-").
Olivier Dulac,

... e non è una cattiva pratica avere file con nuove righe e schede nei loro nomi? So io cerco di nomi limite a [a-z0-9.+-].
Blacklight Shining

5
@BlacklightShining, è molto brutto rubare auto, ma è male lasciare la tua auto sbloccata (ignora le nuove righe), soprattutto quando si tratta di un'auto costosa (script eseguito come utente privilegiato, su un server con dati sensibili ...) o quando si parcheggiarla in un'area ruvida ( /tmp) o in un'area con molte macchine costose ( $HOME) ed è anche peggio andare in un sito di domande e risposte e dire che va sempre bene non bloccare l'auto senza specificare in quali condizioni (in un garage chiuso, sceneggiatura hai scritto gestito da solo solo su una macchina non connessa a nessuna rete o memoria rimovibile ...)
Stéphane Chazelas

1
@BlacklightShining, le newline sono insolite ma gli spazi incorporati sono molto comuni al giorno d'oggi, in particolare per i file creati tramite GUI.
alexis

2
@BlacklightShining, sì, anche se uno (come "b "o "a\bb") ingannerebbe un utente su un terminale ma non uno script che analizza l'output di du ./*. Probabilmente dovrei aggiungere una nota a riguardo. Lo farà domani. Si noti che prima intendevo privilegiato in senso generale, non root(sebbene si applichi soprattutto a rootovviamente). le nuove righe sono consentite, ignorarle è un bug. i bug hanno l'abitudine di essere sfruttati. Devi misurare il rischio caso per caso. Le buone pratiche di codifica possono evitare i problemi in molti casi. Certamente su SE, dovremmo sensibilizzare.
Stéphane Chazelas il

6

Non vi è alcuna differenza tra a *e ./*in termini di quali file verranno elencati. L'unica differenza sarebbe con la seconda forma, ogni file avrebbe una barra di punti con il ./prefisso davanti a loro, che in genere significa la directory corrente.

Ricorda che la .directory è una notazione abbreviata per la directory corrente.

$ ls -la | head -4
total 28864
drwx------. 104 saml saml    12288 Jan 23 20:04 .
drwxr-xr-x.   4 root root     4096 Jul  8  2013 ..
-rw-rw-r--.   1 saml saml      972 Oct  6 20:26 abcdefg

Puoi convincerti che questi 2 elenchi sono essenzialmente la stessa cosa usando echoper vedere a cosa la shell li espanderebbe.

$ echo *
$ echo ./*

Questi 2 comandi elencheranno tutti i file nella directory corrente.

Esempi

Possiamo creare alcuni dati falsi in questo modo:

$ touch file{1..5}
$ ll
total 0
-rw-rw-r--. 1 saml saml 0 Jan 24 07:14 file1
-rw-rw-r--. 1 saml saml 0 Jan 24 07:14 file2
-rw-rw-r--. 1 saml saml 0 Jan 24 07:14 file3
-rw-rw-r--. 1 saml saml 0 Jan 24 07:14 file4
-rw-rw-r--. 1 saml saml 0 Jan 24 07:14 file5

Ora quando usiamo i echocomandi sopra vediamo il seguente output:

$ echo *
file1 file2 file3 file4 file5
$ echo ./*
./file1 ./file2 ./file3 ./file4 ./file5

Questa differenza può sembrare superflua, ma ci sono situazioni in cui vuoi garantire ai vari strumenti della riga di comando Unix che stai passando loro nomi di file tramite la riga di comando, e niente di più!

Allora perché usare ./*?

Come sottolinea la risposta di @ Stephane , a causa della natura di ciò che i personaggi sono legali quando si nominano file e directory in Unix, si possono costruire nomi di file pericolosi che hanno effetti collaterali inattesi quando vengono passati a vari comandi Unix dalla riga di comando.

Così spesso l'uso di ./sarà usato per aiutare a garantire che i nomi di file espansi siano considerati nomi di file quando passati come argomenti ai vari comandi Unix.

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.