Was ist der Unterschied zwischen du -sh *
und du -sh ./*
?
Hinweis:Was mich interessiert, ist der *
und ./*
Teile.
Akzeptierte Antwort:
$ touch ./-c $'an12tb' foo $ du -hs * 0 a 12 b 0 foo 0 total
Wie Sie sehen können, ist der -c
Datei wurde als Option zu du
genommen und wird nicht gemeldet (und Sie sehen die total
). Zeile wegen du -c
). Auch die Datei namens an12tb
lässt uns glauben, dass es Dateien namens a
gibt und b
.
$ du -hs -- *
0 a
12 b
0 -c
0 foo
Das ist besser. Zumindest diesmal -c
wird nicht als Option genommen.
$ du -hs ./*
0 ./a
12 b
0 ./-c
0 ./foo
Das ist sogar noch besser. Der ./
Präfix verhindert -c
nicht als Option genommen werden und das Fehlen von ./
vor b
in der Ausgabe gibt an, dass kein b
vorhanden ist Datei darin, aber es gibt eine Datei mit einem Zeilenumbruchzeichen (aber siehe unten für weitere Abschweifungen dazu).
Es hat sich bewährt, ./
zu verwenden Präfix wenn möglich, und wenn nicht und für beliebige Daten, sollten Sie immer verwenden:
cmd -- "$var"
oder:
cmd -- $patterns
Wenn cmd
unterstützt --
nicht Um das Ende der Optionen zu markieren, sollten Sie es dem Autor als Fehler melden (außer wenn es freiwillig und dokumentiert ist wie für echo
).
Es gibt Fälle, in denen ./*
löst Probleme, die --
nicht. Zum Beispiel:
awk -f file.awk -- *
schlägt fehl, wenn es eine Datei namens a=b.txt
gibt im aktuellen Verzeichnis (setzt die awk-Variable a
zu b.txt
anstatt ihm zu sagen, dass es die Datei verarbeiten soll).
awk -f file.awk ./*
Hat das Problem nicht, weil ./a
ist kein gültiger awk-Variablenname, also ./a=b.txt
wird nicht als Variablenzuweisung genommen.
cat -- * | wc -l
schlägt fehl, wenn es eine Datei namens -
gibt im aktuellen Verzeichnis, wie das cat
mitteilt aus seiner stdin zu lesen (-
ist speziell für die meisten Textverarbeitungsprogramme und für cd
/pushd
).
cat ./* | wc -l
ist in Ordnung, weil ./-
ist nichts Besonderes für cat
.
Dinge wie:
grep -l -- foo *.txt | wc -l
um die Anzahl der Dateien zu zählen, die foo
enthalten sind falsch, weil davon ausgegangen wird, dass Dateinamen keine Zeilenumbrüche enthalten (wc -l
zählt die Newline-Zeichen, die von grep
ausgegeben werden für jede Datei und die in den Dateinamen selbst). Sie sollten stattdessen verwenden:
grep -l foo ./*.txt | grep -c /
(Zählen der Anzahl von /
Zeichen ist zuverlässiger, da es nur eines pro Dateiname geben kann).
Für rekursives grep
, ist der entsprechende Trick die Verwendung von:
grep -rl foo .//. | grep -c //
./*
kann jedoch einige unerwünschte Nebenwirkungen haben.
cat ./*
fügt zwei weitere Zeichen pro Datei hinzu, sodass Sie die Grenze der maximalen Größe von arguments+environment früher erreichen würden. Und manchmal möchten Sie diesen ./
nicht in der Ausgabe gemeldet werden. Wie:
grep foo ./*
Würde ausgeben:
./a.txt: foobar
statt:
a.txt: foobar
Weitere Abschweifungen
. Ich habe das Gefühl, dass ich das hier weiter ausführen muss, wenn ich der Diskussion in den Kommentaren folge.
$ du -hs ./*
0 ./a
12 b
0 ./-c
0 ./foo
Oben, dieser ./
Das Markieren des Anfangs jeder Datei bedeutet, dass wir klar erkennen können, wo jeder Dateiname beginnt (bei ./
) und wo es endet (beim Zeilenumbruch vor dem nächsten ./
oder das Ende der Ausgabe).
Das bedeutet, dass die Ausgabe von du ./*
, im Gegensatz zu du -- *
) kann zuverlässig geparst werden, wenn auch nicht so einfach in einem Skript.
Wenn die Ausgabe jedoch an ein Terminal geht, gibt es noch viel mehr Möglichkeiten, wie ein Dateiname Sie täuschen kann:
-
Steuerzeichen und Escape-Sequenzen können die Darstellung beeinflussen. Zum Beispiel
r
bewegt den Cursor zum Anfang der Zeile,b
bewegt den Cursor zurück,e[C
weiterleiten (in den meisten Terminals)… -
Viele Zeichen sind auf einem Terminal unsichtbar, beginnend mit dem offensichtlichsten:dem Leerzeichen.
-
Es gibt Unicode-Zeichen, die genauso aussehen wie der Schrägstrich in den meisten Schriftarten
$ printf 'u002f u2044 u2215 u2571 u29F8n' / ⁄ ∕ ╱ ⧸
(Sehen Sie, wie es in Ihrem Browser läuft).
Ein Beispiel:
$ 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
Viele x
ist aber y
fehlt.
Einige Tools wie GNU
ls würde die nicht druckbaren Zeichen durch ein Fragezeichen ersetzen (beachten Sie, dass ∕
(U+2215) ist jedoch druckbar), wenn die Ausgabe an ein Terminal geht. GNU du
nicht.
Es gibt Möglichkeiten, sie sich offenbaren zu lassen:
$ 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
Siehe wie ∕
wurde zu ???
nachdem wir ls
gesagt haben dass unser Zeichensatz ASCII war.
$ du -hs ./* | LC_ALL=C sed -n l0t./x$0t./x $0t./x$0t.342210225x$0t./yr0t.