die Ausgabe ? man wird es dir zeigen ./ vor dem Dateinamen
Kiwy
Antworten:
69
$ touch ./-c $ 'a \ n12 \ tb' foo
$ du -hs *
0 a
12 b
0 foo
0 insgesamt
Wie Sie sehen, wurde die -cDatei als Option für übernommen duund wird nicht gemeldet (und Sie sehen die totalZeile wegen du -c). Die aufgerufene Datei a\n12\tblässt uns auch denken, dass es Dateien mit dem Namen aund gibt b.
$ du -hs --*0 a12 b0-c0 foo
Das ist besser. Zumindest wird diese Zeit -cnicht als Option angenommen.
$ du -hs ./*0./a12 b0./-c0./foo
Das ist sogar noch besser. Das ./Präfix verhindert, -cdass es als Option verwendet wird, und das Fehlen von " ./Vorher" bin der Ausgabe zeigt an, dass keine bDatei 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 cmdes 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.txtim aktuellen Verzeichnis aufgerufen wird (setzt die Variable awk aauf, b.txtanstatt sie anzuweisen, die Datei zu verarbeiten).
awk -f file.awk ./*
Hat das Problem nicht, da ./aes sich nicht um einen gültigen awk-Variablennamen ./a=b.txthandelt, der nicht als Variablenzuweisung verwendet wird.
cat --*| wc -l
schlägt fehl, wenn -im aktuellen Verzeichnis eine Datei aufgerufen wird , die catdas 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 foozählen ist falsch, da davon ausgegangen wird, dass Dateinamen keine Zeilenumbrüche enthalten ( wc -lZählt die Zeilenumbrüche, die von grepjeder 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 grepist 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
Weitere Abschweifungen
1 . Ich habe das Gefühl, dass ich hier nach der Diskussion in Kommentaren darauf eingehen muss.
$ du -hs ./*0./a12 b0./-c0./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:
Steuerzeichen und Escape-Sequenzen können die Darstellung beeinflussen. \rBewegt zum Beispiel den Cursor an den Zeilenanfang, \bbewegt den Cursor zurück, \e[Cvorwärts (in den meisten Terminals) ...
Viele Zeichen sind auf einem Terminal unsichtbar, beginnend mit dem offensichtlichsten: dem Leerzeichen.
Es gibt Unicode-Zeichen, die in den meisten Schriftarten genauso aussehen wie der Schrägstrich
$ touch x 'x ' $'y\bx' $'x\n0\t.\u2215x' $'y\r0\t.\e[Cx'
$ ln x y
$ du -hs ./*0./x0./x0./x0.∕x0./x0./x
Viele x‚s aber yfehlt.
Einige Tools wie GNUls 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 dunicht.
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, lsdass unser Zeichensatz ASCII war.
$ du -hs ./*| LC_ALL=C sed -n l0\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 sedImplementierungen gleich sein, aber beachten Sie, dass einige alte sedImplementierungen bei weitem nicht so hilfreich sind.
$ du -hs ./*| cat -vte0^I./x$0^I./x $0^I./x$0^I.M-bM-^HM-^Ux$
(Nicht Standard, aber ziemlich häufig, auch cat -Abei einigen Implementierungen). Dieser ist hilfreich und verwendet eine andere Darstellung, ist jedoch mehrdeutig ( "^I"und wird <TAB>beispielsweise gleich angezeigt).
$ du -hs ./*| od -vtc00000000 \t ./ x \n 0 \t ./ x \n 0 \t .0000020/ x \n 0 \t .342210225 x \n 0 \t ./ y0000040 \r 0 \t .033[ C x \n 0 \t ./ y \b x0000060 \n0000061
Das ist Standard und eindeutig (und von Implementierung zu Implementierung konsistent), aber nicht so einfach zu lesen.
Sie werden bemerken, dass ydas 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 duBerichte Festplattennutzung, spielt es keine andere Verbindungen berichtet in eine Datei bereits aufgelistet (nicht alle duImplementierungen verhalten sich wie , dass , obwohl , wenn die harten Links aufgeführt sind auf der Kommandozeile).
+1, nett und gründlich (soweit ich das beurteilen kann ^^). Ich mag besonders den "grep -c /" Vorteil. Ebenfalls erwähnenswert: Der Vorteil von "./*" gegenüber "*" zeigt sich in einer der (vielen) guten Antworten der Unix-FAQ (wahrscheinlich auf faqs.org. Iirc, es geht um rm-ing-Dateien, die mit beginnen) ein "-").
Olivier Dulac
… Und es ist keine schlechte Praxis, Dateien mit Zeilenumbrüchen und Tabulatoren im Namen zu haben? Ich weiß, ich versuche, Namen zu begrenzen [a-z0-9.+-].
Blacklight Shining
5
@BlacklightShining, es ist sehr schlecht , Autos zu stehlen, aber es ist schlecht , Ihr Auto unverschlossen zu lassen (Zeilenumbrüche zu ignorieren), besonders wenn es sich um ein teures Auto handelt (Skript, das als privilegierter Benutzer ausgeführt wird, auf einem Server mit vertraulichen Daten ...) oder wenn Sie Parken Sie es in einem unwegsamen Gebiet ( /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 ...)
Stéphane Chazelas
1
@BlacklightShining, Zeilenumbrüche sind ungewöhnlich, aber eingebettete Leerzeichen sind heutzutage sehr verbreitet, insbesondere für Dateien, die über die GUI erstellt wurden.
Alexis
2
@BlacklightShining, ja obwohl dieses (wie "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 rootnatü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.
Stéphane Chazelas
6
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 12288Jan2320:04.
drwxr-xr-x.4 root root 4096Jul82013..-rw-rw-r--.1 saml saml 972Oct620:26 abcdefg
Sie können sich davon überzeugen, dass diese beiden Listen im Wesentlichen dasselbe sind, indem Sie echosehen, wie die Shell sie erweitern würde.
$ echo *
$ echo ./*
Mit diesen beiden Befehlen werden alle Dateien in Ihrem aktuellen Verzeichnis aufgelistet.
Beispiele
Wir können einige gefälschte Daten wie folgt erstellen:
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!
Warum also ./* verwenden?
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.
Antworten:
Wie Sie sehen, wurde die
-c
Datei als Option für übernommendu
und wird nicht gemeldet (und Sie sehen dietotal
Zeile wegendu -c
). Die aufgerufene Dateia\n12\tb
lässt uns auch denken, dass es Dateien mit dem Namena
und gibtb
.Das ist besser. Zumindest wird diese Zeit
-c
nicht als Option angenommen.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 keineb
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:oder:
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 istecho
).Es gibt Fälle, in denen
./*
Probleme gelöst werden, bei denen--
dies nicht der Fall ist. Zum Beispiel:schlägt fehl, wenn eine Datei
a=b.txt
im aktuellen Verzeichnis aufgerufen wird (setzt die Variable awka
auf,b.txt
anstatt sie anzuweisen, die Datei zu verarbeiten).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.schlägt fehl, wenn
-
im aktuellen Verzeichnis eine Datei aufgerufen wird , diecat
das Lesen von der Standardeingabe anweist (-
ist für die meisten Textverarbeitungsprogramme und fürcd
/ besonderspushd
).ist ok da
./-
ist nichts besonderes drancat
.Dinge wie:
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 vongrep
jeder Datei ausgegebenen und die in den Dateinamen selbst enthaltenen). Sie sollten stattdessen verwenden:(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:./*
kann jedoch einige unerwünschte Nebenwirkungen haben.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:Würde ausgeben:
anstatt:
Weitere Abschweifungen
1 . Ich habe das Gefühl, dass ich hier nach der Diskussion in Kommentaren darauf eingehen muss.
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 zudu -- *
) 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
(Sehen Sie, wie es in Ihrem Browser geht).
Ein Beispiel:
Viele
x
‚s abery
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. GNUdu
nicht.Es gibt Möglichkeiten, wie sie sich offenbaren können:
Sehen Sie, wie es
∕
weiterging,???
nachdem wir festgestellt hatten,ls
dass unser Zeichensatz ASCII war.$
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 GNUsed
, es sollte in allen POSIX-kompatiblensed
Implementierungen gleich sein, aber beachten Sie, dass einige altesed
Implementierungen bei weitem nicht so hilfreich sind.(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).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: weildu
Berichte Festplattennutzung, spielt es keine andere Verbindungen berichtet in eine Datei bereits aufgelistet (nicht alledu
Implementierungen verhalten sich wie , dass , obwohl , wenn die harten Links aufgeführt sind auf der Kommandozeile).quelle
[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 parstdu ./*
. Ich sollte wahrscheinlich eine Anmerkung dazu hinzufügen. Werde morgen machen. Beachten Sie, dass ich früher Privilegierte im allgemeinen Sinne meinte , nichtroot
(obwohl diesroot
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.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.Mit diesen beiden Befehlen werden alle Dateien in Ihrem aktuellen Verzeichnis aufgelistet.
Beispiele
Wir können einige gefälschte Daten wie folgt erstellen:
Wenn wir nun die obigen
echo
Befehle verwenden, sehen wir die folgende Ausgabe: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!
Warum also ./* verwenden?
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.quelle