In einem Skript find
sammle ich einige Dateien im aktuellen Verzeichnis, wie in
$ find . -name "*.h"
./foo.h
Jetzt möchte ich, dass es nur foo.h
ohne ./
Präfix ausgegeben wird . Ich dachte, dass die leere Zeichenfolge ""
das aktuelle Verzeichnis in Shell-Befehlen bezeichnet. Aber das gibt:
$ find "" -name "*.h"
find: ftsopen: No such file or directory
Also habe ich mich geirrt. Meine Frage ist nun, wann / wie / wo / .. eine "leere Zeichenfolge (?)" Das aktuelle Verzeichnis in Befehlen bezeichnet, die einen Dateinamen oder einen Pfadnamen erwarten. Gibt es eine ordentliche und aufschlussreiche Erklärung?
Eine Nebenfrage ist, ob das obige Find Nitpicking einfach gelöst werden kann, ohne String-Manipulation a la ${parameter#word}
oder cut
oder sed
?
find
Mit dem Parameter -printf können Sie festlegen, wie die Ergebnisse angezeigt werden.find . -name '*.h' -printf '%P\n'
entfernt das./
Präfix. Sehen Sie, wasfind . -name '*.h' -print
tut.-printf
Option nicht unterstützt . FWIW,strings
auf meinem Fund gibt@(#)PROGRAM:find PROJECT:shell_cmds-175
?!?Antworten:
Vor langer Zeit (in der 7. Ausgabe , 32V , 4.2BSD , 4.3BSD ) bezeichnete auf Systemaufrufebene ein Pfadname mit der Länge Null das aktuelle Arbeitsverzeichnis (wenn es für die Suche verwendet wurde, wurde es beim Versuch, ein zu erstellen oder zu löschen, nicht zugelassen Datei oder Verzeichnis). In System III war es ein Fehler, unter allen Umständen einen Pfadnamen mit der Länge Null zu verwenden, und der POSIX-Standard hat Folgendes zur Auflösung des Pfadnamens zu sagen:
quelle
dir/
ist meistens das gleiche wiedir/.
Sie können verwenden:
Beachten Sie, dass Dateien im aktuellen Verzeichnis, deren Name mit beginnt
.
, weggelassen werden und Dateien, deren Name mit beginnt-
, als Optionen von interpretiert werdenfind
und Chaos verursachen. Dies ist also kein allgemeines Äquivalent zufind . …
.Das Fehlen einer Zeichenkette in „endet
/
“ als Teil eines Dateinamen impliziert das aktuelle Verzeichnis, aber das bedeutet nicht , das aktuelle Verzeichnis wird bezeichnet durch einen leeren String (was wohl nicht das gleiche wie das Fehlen einer Zeichenfolge ist, obwohl es beim Drucken gleich aussehen könnte).quelle
Im Allgemeinen bezeichnet die leere Zeichenfolge weder für Shell-Befehle noch für Systemaufrufe das aktuelle Verzeichnis. Dies war auf einigen älteren Systemen der Fall, jedoch nicht auf POSIX-kompatiblen Systemen .
Gelegentlich finden Sie ein Programm, das das aktuelle Verzeichnis verwendet, wenn Sie eine leere Zeichenfolge übergeben und das Programm einen Verzeichnisnamen erwartet. Dies ist manchmal absichtlich und manchmal ein Nebeneffekt, wenn dem absoluten Pfad des aktuellen Verzeichnisses vorangestellt wird, wenn die angegebene Zeichenfolge nicht mit einem Schrägstrich beginnt.
Das Beste für Sie wäre, das zu verlassen
./
. Es schadet nicht.Wenn die Liste der Dateien für ist
Beachten Sie, dass dadurch einige Dateinamen, die Zeilenumbrüche enthalten, beschädigt werden. Dies ist normalerweise kein Problem für den menschlichen Verzehr, und die Ausgabe von
find
ist nicht für den Programmverbrauch geeignet, da sie nicht eindeutig ist. Wenn Sie verwenden-print0
, was für den Programmverbrauch geeignet ist, ist Ihnen das./
Präfix wahrscheinlich sowieso egal .Sie können
find * …
anstelle von verwendenfind . …
, beachten Sie jedoch, dassfind *
es eine Reihe von Fehlern gibt, die es im Allgemeinen ungeeignet machen:.
wurde weggelassen..
Punktdateien (Dateien, deren Name mit oder ..` beginnt ) werden weggelassen.-
(oder einer Datei mit dem Namen!
oder(
...) beginnt , wird dieser als Option oder Prädikat von interpretiertfind
.Der erste Punkt spielt keine Rolle, wenn Ihr Filter das aktuelle Verzeichnis ausschließt. Für den zweiten Punkt können Sie die Muster verwenden
..?* .[!.]* *
, um alle Dateien im aktuellen Verzeichnis abzugleichen. Sie müssen jedoch überprüfen, ob jedes Muster mit mindestens einer Datei übereinstimmt, und es weglassen, wenn dies nicht der Fall ist. Dies ist möglich, aber sehr umständlich. Der letzte Punkt ist ein Stopper. Sofind *
geeignet sein kann für Quickie Befehlszeile verwenden, aber Sie es nicht in einem Skript verwenden.Ein alternativer Ansatz besteht darin, die rekursive Globbing-Funktion der Shell zu verwenden, z
Dies muss von
shopt -s globstar
in bash und vonset -o globstar
in ksh93 aktiviert werden und ist in einer grundlegenden POSIX-Shell wie dash nicht vorhanden. Punktdateien werden standardmäßig nicht durchlaufen. Um sie einzuschließen, lassen Sie das Globbing zuerst Punktdateien mitshopt -s dotglob
in bash oderFIGNORE='@(.|..)'
in ksh93 nicht ignorieren . Wenn keine Übereinstimmungen vorhanden sind, gibt dieser Befehl das Muster aus. Führen Sieshopt -s nullglob
in bash aus, um stattdessen eine leere Zeile zu drucken, und verwenden Sie das Muster~(N)**/*.h
in ksh.In zsh ist rekursives Globbing standardmäßig aktiviert. Verwenden Sie das Glob-Qualifikationsmerkmal
D
, um Punktdateien einzuschließen undN
eine leere Zeile zu drucken, wenn keine Übereinstimmungen vorliegen (standardmäßig gibt zsh einen Fehler aus, wenn ein Muster keiner Datei entspricht). Sie könnenprintf
wie oben oder verwendenquelle
find *
sind für mich neu und einige sind erschreckend !!Ich kann mir kein Beispiel vorstellen, bei dem die leere Zeichenfolge das aktuelle Verzeichnis bezeichnet
.
. Sie denken vielleicht an Aufrufe wiels
, aber das liegt daran,ls
dass das aktuelle Verzeichnis angenommen wird, wenn kein Parameter angegeben ist, und tatsächlich wird die leere Zeichenfolge nicht verwendet:quelle
PATH
Komponente.Es gibt verschiedene Tricks, mit denen Sie die Ausgabe von find ohne den führenden erhalten können
./
:Verwenden Sie die
-printf
Option find und weisen Sie sie an, nur zu drucken%f
. Sieheman find
:Zum Beispiel:
Analysieren Sie die Ausgabe:
@ Anthons Trick
Bei Ihrer anderen Frage gibt die leere Zeichenfolge niemals das aktuelle Verzeichnis an. Es ist nur so, dass verschiedene Programme das aktuelle Verzeichnis als Standard verwenden. Wenn Sie sie also ohne Argumente ausführen, werden sie im aktuellen Verzeichnis ausgeführt.
quelle
find
. Oder verwenden Sie,%P
um nur die./
find * -name '*.h'
wird findenfoo/.bar.h
, aber nicht.bar.h
im aktuellen Verzeichnis.Wenn sich die Dateien im aktuellen Verzeichnis befinden, können Sie einfach Folgendes verwenden
ls
:Wenn Sie die Dateien auch in Unterverzeichnissen haben möchten und nur den Dateinamen benötigen, können Sie Folgendes tun:
Es gibt Befehle, bei denen keine Zeichenfolge als aktuelles Verzeichnis angezeigt wird. Grundsätzlich wird jedoch das aktuelle Verzeichnis standardmäßig abgerufen, wenn nichts eingegeben wird. Die
.
bezeichnen immer das aktuelle..
Verzeichnis (und das übergeordnete Verzeichnis).quelle
find
Lösung alle Verzeichniskomponenten entfernt