Antworten:
$ touch ./-c $ 'a \ n12 \ tb' foo $ du -hs * 0 a 12 b 0 foo 0 insgesamt
Wie Sie sehen, wurde die -c
Datei als Option für übernommen du
und wird nicht gemeldet (und Sie sehen die total
Zeile wegen du -c
). Die aufgerufene Datei a\n12\tb
lässt uns auch denken, dass es Dateien mit dem Namen a
und gibt b
.
$ du -hs -- *
0 a
12 b
0 -c
0 foo
Das ist besser. Zumindest wird diese Zeit -c
nicht als Option angenommen.
$ du -hs ./*
0 ./a
12 b
0 ./-c
0 ./foo
Das ist sogar noch besser. Das ./
Präfix verhindert, -c
dass es als Option verwendet wird, und das Fehlen von " ./
Vorher" b
in der Ausgabe zeigt an, dass keine b
Datei vorhanden ist, aber eine Datei mit einem Zeilenumbruchzeichen ( weitere Informationen hierzu finden Sie unter 1 ).
Es wird empfohlen, das ./
Präfix zu verwenden, wenn dies möglich ist. Wenn dies nicht der Fall ist und für beliebige Daten, sollten Sie immer Folgendes verwenden:
cmd -- "$var"
oder:
cmd -- $patterns
Wenn cmd
es nicht unterstützt wird --
, das Ende von Optionen zu markieren, sollten Sie es dem Autor als Fehler melden (außer wenn es nach Wahl und wie für dokumentiert ist echo
).
Es gibt Fälle, in denen ./*
Probleme gelöst werden, bei denen --
dies nicht der Fall ist. Zum Beispiel:
awk -f file.awk -- *
schlägt fehl, wenn eine Datei a=b.txt
im aktuellen Verzeichnis aufgerufen wird (setzt die Variable awk a
auf, b.txt
anstatt sie anzuweisen, die Datei zu verarbeiten).
awk -f file.awk ./*
Hat das Problem nicht, da ./a
es sich nicht um einen gültigen awk-Variablennamen ./a=b.txt
handelt, der nicht als Variablenzuweisung verwendet wird.
cat -- * | wc -l
schlägt fehl, wenn -
im aktuellen Verzeichnis eine Datei aufgerufen wird , die cat
das Lesen von der Standardeingabe anweist ( -
ist für die meisten Textverarbeitungsprogramme und für cd
/ besonders pushd
).
cat ./* | wc -l
ist ok da ./-
ist nichts besonderes dran cat
.
Dinge wie:
grep -l -- foo *.txt | wc -l
Die Anzahl der enthaltenen Dateien zu foo
zählen ist falsch, da davon ausgegangen wird, dass Dateinamen keine Zeilenumbrüche enthalten ( wc -l
Zählt die Zeilenumbrüche, die von grep
jeder Datei ausgegebenen und die in den Dateinamen selbst enthaltenen). Sie sollten stattdessen verwenden:
grep -l foo ./*.txt | grep -c /
(Die Anzahl der /
Zeichen zu zählen ist zuverlässiger, da es nur eine pro Dateiname geben kann).
Für rekursive grep
ist der äquivalente Trick zu verwenden:
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 Argumenten + Umgebung schneller erreichen. Und manchmal möchten Sie nicht, dass ./
dies in der Ausgabe gemeldet wird. Mögen:
grep foo ./*
Würde ausgeben:
./a.txt: foobar
anstatt:
a.txt: foobar
1 . Ich habe das Gefühl, dass ich hier nach der Diskussion in Kommentaren darauf eingehen muss.
$ du -hs ./*
0 ./a
12 b
0 ./-c
0 ./foo
Darüber hinaus bedeutet diese ./
Markierung des Anfangs jeder Datei, dass wir klar identifizieren können, wo jeder Dateiname beginnt (bei ./
) und wo er endet (in der neuen Zeile vor dem nächsten ./
oder dem Ende der Ausgabe).
Das bedeutet, dass die Ausgabe von du ./*
im Gegensatz zu du -- *
) zuverlässig analysiert werden kann, wenn auch nicht so einfach in einem Skript.
Wenn die Ausgabe jedoch an ein Terminal geht, kann ein Dateiname Sie auf viele weitere Arten täuschen:
\r
Bewegt zum Beispiel den Cursor an den Zeilenanfang, \b
bewegt den Cursor zurück, \e[C
vorwärts (in den meisten Terminals) ...Es gibt Unicode-Zeichen, die in den meisten Schriftarten genauso aussehen wie der Schrägstrich
$ printf '\u002f \u2044 \u2215 \u2571 \u29F8\n'
/ ⁄ ∕ ╱ ⧸
(Sehen Sie, wie es in Ihrem Browser geht).
Ein Beispiel:
$ 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
Viele x
‚s aber y
fehlt.
Einige Tools wie GNU
ls ersetzen die nicht druckbaren Zeichen durch ein Fragezeichen (beachten Sie jedoch, dass ∕
(U + 2215) druckbar ist), wenn die Ausgabe an ein Terminal gesendet wird. GNU du
nicht.
Es gibt Möglichkeiten, wie sie sich offenbaren können:
$ 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
Sehen Sie, wie es ∕
weiterging, ???
nachdem wir festgestellt hatten, ls
dass unser Zeichensatz ASCII war.
$ 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$
$
markiert das Ende der Zeile, damit wir das "x"
vs erkennen "x "
können. Alle nicht druckbaren Zeichen und Nicht-ASCII-Zeichen werden durch eine Backslash-Sequenz dargestellt (Backslash selbst würde mit zwei Backslashes dargestellt), was bedeutet, dass es eindeutig ist. Das war GNU sed
, es sollte in allen POSIX-kompatiblen sed
Implementierungen gleich sein, aber beachten Sie, dass einige alte sed
Implementierungen bei weitem nicht so hilfreich sind.
$ du -hs ./* | cat -vte
0^I./x$
0^I./x $
0^I./x$
0^I.M-bM-^HM-^Ux$
(Nicht Standard, aber ziemlich häufig, auch cat -A
bei einigen Implementierungen). Dieser ist hilfreich und verwendet eine andere Darstellung, ist jedoch mehrdeutig ( "^I"
und wird <TAB>
beispielsweise gleich angezeigt).
$ 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
Das ist Standard und eindeutig (und von Implementierung zu Implementierung konsistent), aber nicht so einfach zu lesen.
Sie werden bemerken, dass y
das oben nie aufgetaucht ist. Das ist ein völlig unabhängiges Problem mit , du -hs *
dass nichts mit Dateinamen zu tun hat, soll aber beachtet werden: weil du
Berichte Festplattennutzung, spielt es keine andere Verbindungen berichtet in eine Datei bereits aufgelistet (nicht alle du
Implementierungen verhalten sich wie , dass , obwohl , wenn die harten Links aufgeführt sind auf der Kommandozeile).
[a-z0-9.+-]
.
/tmp
) oder in einem Gebiet mit vielen teuren Autos ( $HOME
), und es ist noch schlimmer, wenn Sie zu einer Frage- und Antwort-Website gehen und sagen, dass es immer in Ordnung ist, Ihr Auto nicht zu sperren, ohne anzugeben, unter welchen Bedingungen (in einer verschlossenen Garage, Skript) Sie haben selbst nur auf einem Computer geschrieben, der nicht mit einem Netzwerk oder Wechseldatenträger verbunden ist ...)
"b "
oder "a\bb"
) einen Benutzer auf einem Terminal täuschen würde, aber kein Skript, das die Ausgabe von parst du ./*
. Ich sollte wahrscheinlich eine Anmerkung dazu hinzufügen. Werde morgen machen. Beachten Sie, dass ich früher Privilegierte im allgemeinen Sinne meinte , nicht root
(obwohl dies root
natürlich umso mehr zutrifft ). Zeilenumbrüche sind erlaubt, das Ignorieren ist ein Fehler. Fehler haben die Angewohnheit, ausgenutzt zu werden. Sie müssen das Risiko von Fall zu Fall messen. Durch eine gute Codierungspraxis können die Probleme in vielen Fällen vermieden werden. Auf jeden Fall sollten wir auf SE aufmerksam machen.
Es gibt keinen Unterschied zwischen a *
und ./*
hinsichtlich der Liste der Dateien. Der einzige Unterschied besteht darin, dass jeder Datei ein Schrägstrich ./
vorangestellt ist, der normalerweise das aktuelle Verzeichnis bezeichnet.
Denken Sie daran, dass das .
Verzeichnis eine Kurzschreibweise für das aktuelle Verzeichnis ist.
$ 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
Sie können sich davon überzeugen, dass diese beiden Listen im Wesentlichen dasselbe sind, indem Sie echo
sehen, wie die Shell sie erweitern würde.
$ echo *
$ echo ./*
Mit diesen beiden Befehlen werden alle Dateien in Ihrem aktuellen Verzeichnis aufgelistet.
Wir können einige gefälschte Daten wie folgt erstellen:
$ 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
Wenn wir nun die obigen echo
Befehle verwenden, sehen wir die folgende Ausgabe:
$ echo *
file1 file2 file3 file4 file5
$ echo ./*
./file1 ./file2 ./file3 ./file4 ./file5
Dieser Unterschied mag unnötig erscheinen, aber es gibt Situationen, in denen Sie den verschiedenen Unix-Befehlszeilentools garantieren möchten, dass Sie ihnen Dateinamen über die Befehlszeile übergeben, und sonst nichts!
In der Antwort von @ Stephane heißt es , dass aufgrund der Art und Weise, wie Zeichen bei der Benennung von Dateien und Verzeichnissen in Unix zulässig sind, gefährliche Dateinamen erstellt werden können, die unerwartete Nebenwirkungen haben, wenn sie an verschiedene Unix-Befehle in der Befehlszeile übergeben werden.
So oft wird die Verwendung von ./
verwendet, um sicherzustellen, dass erweiterte Dateinamen als Dateinamen betrachtet werden, wenn sie als Argumente an die verschiedenen Unix-Befehle übergeben werden.