Qual è la differenza tra du -sh * e du -sh ./* ?
Nota:quello che mi interessa è il * e ./* parti.
Risposta accettata:
$ touch ./-c $'an12tb' foo $ du -hs * 0 a 12 b 0 foo 0 total
Come puoi vedere, il -c il file è stato preso come opzione per du e non è riportato (e vedi il total riga a causa di du -c ). Inoltre, il file chiamato an12tb ci sta facendo pensare che ci siano dei file chiamati a e b .
$ du -hs -- *
0 a
12 b
0 -c
0 foo
Va meglio. Almeno questa volta -c non è considerata un'opzione.
$ du -hs ./*
0 ./a
12 b
0 ./-c
0 ./foo
È anche meglio. Il ./ prefisso impedisce -c dall'essere preso come opzione e dall'assenza di ./ prima di b nell'output indica che non c'è nessun b file lì dentro, ma c'è un file con un carattere di nuova riga (ma vedi sotto per ulteriori digressioni su questo).
È buona norma utilizzare il ./ prefisso quando possibile, e in caso contrario e per dati arbitrari, dovresti sempre usa:
cmd -- "$var"
oppure:
cmd -- $patterns
Se cmd non supporta -- per segnare la fine delle opzioni, dovresti segnalarlo come bug al suo autore (tranne quando è per scelta e documentato come per echo ).
Ci sono casi in cui ./* risolve problemi che -- no. Ad esempio:
awk -f file.awk -- *
fallisce se è presente un file chiamato a=b.txt nella directory corrente (imposta la variabile awk a a b.txt invece di dirgli di elaborare il file).
awk -f file.awk ./*
Nessun problema perché ./a non è un nome di variabile awk valido, quindi ./a=b.txt non viene considerata come un'assegnazione variabile.
cat -- * | wc -l
fallisce se c'è un file chiamato - nella directory corrente, come dice a cat per leggere dal suo stdin (- è speciale per la maggior parte delle utilità di elaborazione del testo e per cd /pushd ).
cat ./* | wc -l
va bene perché ./- non è speciale per cat .
Cose come:
grep -l -- foo *.txt | wc -l
per contare il numero di file che contengono foo sono sbagliati perché presuppone che i nomi dei file non contengano caratteri di nuova riga (wc -l conta i caratteri di nuova riga, quelli emessi da grep per ogni file e quelli nei nomi dei file stessi). Dovresti usare invece:
grep -l foo ./*.txt | grep -c /
(contando il numero di / caratteri è più affidabile in quanto può essercene solo uno per nome file).
Per grep ricorsivo , il trucco equivalente è usare:
grep -rl foo .//. | grep -c //
./* potrebbe avere alcuni effetti collaterali indesiderati però.
cat ./*
aggiunge altri due caratteri per file, quindi ti farebbe raggiungere prima il limite della dimensione massima di argomenti + ambiente. E a volte non vuoi quel ./ da riportare in output. Come:
grep foo ./*
Verrebbe prodotto:
./a.txt: foobar
invece di:
a.txt: foobar
Ulteriori digressioni
. Sento di doverlo approfondire qui, seguendo la discussione nei commenti.
$ du -hs ./*
0 ./a
12 b
0 ./-c
0 ./foo
Sopra, quel ./ contrassegnare l'inizio di ogni file significa che possiamo identificare chiaramente dove inizia ogni nome di file (in ./ ) e dove finisce (alla nuova riga prima del successivo ./ o alla 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 va a un terminale, tuttavia, ci sono molti altri modi in cui un nome di file può ingannarti:
-
I caratteri di controllo, 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 caratteri sono invisibili su un terminale a partire da quello più ovvio:il carattere spazio.
-
Ci sono caratteri Unicode che hanno lo stesso aspetto della barra nella maggior parte dei caratteri
$ printf 'u002f u2044 u2215 u2571 u29F8n' / ⁄ ∕ ╱ ⧸
(guarda come va nel tuo browser).
Un esempio:
$ touch x 'x ' $'ybx' $'xn0t.u2215x' $'yr0t.e[Cx'
$ ln x y
$ du -hs ./*
0 ./x
0 ./x
0 ./x
0 .∕x
0 ./x
0 ./x
Un sacco di x 's ma y manca.
Alcuni strumenti come GNU ls sostituirà i caratteri non stampabili con un punto interrogativo (notare che ∕ (U+2215) è stampabile) quando l'output va a un terminale. GNU du no.
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 ∕ passato a ??? dopo aver detto a ls che il nostro set di caratteri era ASCII.
$ du -hs ./* | LC_ALL=C sed -n l0t./x$0t./x $0t./x$0t.342210225x$0t./anno0t.