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,
r
sposta il cursore all'inizio della riga,b
sposta il cursore indietro,e[C
avanti (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.