Risposte:
$ touch ./-c $ 'a \ n12 \ tb' pippo $ du -hs * 0 a 12 b 0 pippo 0 totale
Come puoi vedere, il -c
file è stato preso come opzione per du
e non è riportato (e vedi la total
linea a causa di du -c
). Inoltre, il file chiamato ci a\n12\tb
sta facendo pensare che ci siano file chiamati a
e b
.
$ du -hs -- *
0 a
12 b
0 -c
0 foo
Va meglio. Almeno questo tempo -c
non è considerato un'opzione.
$ du -hs ./*
0 ./a
12 b
0 ./-c
0 ./foo
È anche meglio. Il ./
prefisso impedisce -c
di essere preso come opzione e l'assenza di ./
prima b
nell'output indica che non ci sono b
file 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 cmd
non 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.txt
nella directory corrente (imposta la variabile awk a
su b.txt
invece di dirgli di elaborare il file).
awk -f file.awk ./*
Non ha il problema perché ./a
non è un nome di variabile awk valido, quindi ./a=b.txt
non viene preso come assegnazione di variabile.
cat -- * | wc -l
fallisce se esiste un file chiamato -
nella directory corrente, poiché dice cat
di 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 foo
sono errati perché presuppone che i nomi dei file non contengano caratteri di nuova riga ( wc -l
conta i caratteri di nuova riga, quelli generati da grep
ciascun 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
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:
\r
sposta il cursore all'inizio della riga, \b
sposta il cursore indietro, \e[C
avanti (nella maggior parte dei terminali) ...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 x
ma y
mancano.
Alcuni strumenti come GNU
ls sostituiscono i caratteri non stampabili con un punto interrogativo (si noti che ∕
(U + 2215) è stampabile) quando l'output va su 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 ci siamo ∕
rivolti ???
dopo che abbiamo detto ls
che 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 sed
implementazioni conformi a POSIX ma nota che alcune vecchie sed
implementazioni 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 -A
con 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 y
non è 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é du
segnala l'utilizzo del disco, non segnala altri collegamenti a un file già elencato (non tutte le du
implementazioni si comportano in questo modo anche se quando sono elencati i collegamenti reali sulla riga di comando).
[a-z0-9.+-]
.
/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 ...)
"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 root
ovviamente). 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.
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 echo
per vedere a cosa la shell li espanderebbe.
$ echo *
$ echo ./*
Questi 2 comandi elencheranno tutti i file nella directory corrente.
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 echo
comandi 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ù!
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.