Was ist der Unterschied zwischen "du -sh *" und "du -sh ./*"?


26

Was ist der Unterschied zwischen du -sh *und du -sh ./*?

Hinweis: Was mich interessiert, ist das *und ./*Teile.


2
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       a
12      b
0       -c
0       foo

Das ist besser. Zumindest wird diese Zeit -cnicht als Option angenommen.

$ du -hs ./*
0       ./a
12      b
0       ./-c
0       ./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       ./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:

  • 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

    $ 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 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 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 sedImplementierungen gleich sein, aber beachten Sie, dass einige alte sedImplementierungen 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 -Abei 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 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    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 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:

$ 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 echoBefehle 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!

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.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.