Wann bezeichnet die leere Zeichenfolge das aktuelle Verzeichnis?

Wann bezeichnet die leere Zeichenfolge das aktuelle Verzeichnis?

In einem Skript sammle ich findeinige Dateien im aktuellen Verzeichnis, wie in

$ find . -name "*.h"
./foo.h

Jetzt möchte ich, dass es einfach ausgibt foo.h, ohne das ./Präfix. 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 lag ich falsch. Meine Frage ist nun, wann/wie/wo/.. bezeichnet eine „leere Zeichenfolge (?)“ das aktuelle Verzeichnis in Befehlen, die einen Dateinamen oder einen Pfadnamen erwarten? Gibt es eine klare und aufschlussreiche Erklärung?

Eine Nebenfrage ist, ob das oben genannte Haarspalten einfach gelöst werden kann, ohne String-Manipulation à la ${parameter#word}oder cutoder sed?

Antwort1

Vor langer Zeit (im 7. Auflage, 32 V, 4.2BSD, 4.3BSD), auf der Ebene des Systemaufrufs bezeichnete ein Pfadname mit der Länge Null das aktuelle Arbeitsverzeichnis (wenn er für die Suche verwendet wurde; er war nicht zulässig, wenn versucht wurde, eine Datei oder ein Verzeichnis zu erstellen oder zu löschen). In System IIIwar es ein Fehler, unter allen Umständen einen Pfadnamen mit der Länge Null zu verwenden, und diePOSIX-Standardhat Folgendes zur Pfadnamenauflösung zu sagen:

Ein Null-Pfadname kann nicht erfolgreich aufgelöst werden.

Antwort2

Sie können verwenden:

find * -name "*.h"

Beachten Sie, dass Dateien im aktuellen Verzeichnis, deren Name mit beginnt, .ausgelassen werden. Dateien, deren Name mit beginnt, -werden von als Optionen interpretiert findund verursachen Chaos. Dies ist also kein allgemeines Äquivalent zu find . ….

Das Fehlen einer Zeichenfolge mit der Endung " /" als Teil eines Dateinamensimpliziertdas aktuelle Verzeichnis, aber das bedeutet nicht, dass das aktuelle Verzeichnisbezeichnetdurch eine leere Zeichenfolge (was wohl nicht dasselbe ist wie das Fehlen einer Zeichenfolge, obwohl es beim Drucken möglicherweise gleich aussieht).

Antwort3

Im Allgemeinen bezeichnet die leere Zeichenfolge weder bei Shell-Befehlen noch bei Systemaufrufen 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 beabsichtigt und manchmal ein Nebeneffekt davon, den absoluten Pfad des aktuellen Verzeichnisses voranzustellen, wenn die angegebene Zeichenfolge nicht mit einem Schrägstrich beginnt.

Am besten lässt du das stehen ./. Es schadet nicht.

Wenn die Liste der Dateien für

find . … | sed 's!^\./!!'

Beachten Sie, dass dadurch einige Dateinamen mit Zeilenumbrüchen verstümmelt werden. Dies ist für den menschlichen Gebrauch normalerweise kein Problem, und die Ausgabe von findist für den Programmgebrauch nicht geeignet, da sie mehrdeutig ist. Wenn Sie verwenden , was für den Programmgebrauch geeignet ist, ist Ihnen das Präfix -print0wahrscheinlich sowieso egal ../

Sie können find * …anstelle von verwenden find . …, beachten Sie jedoch, dass find *eine Reihe von Mängeln aufweist, die es im Allgemeinen ungeeignet machen:

  • .wurde weggelassen.
  • Alle Dot-Dateien (Dateien deren Name mit .oder ..` beginnt) werden weggelassen.
  • Wenn es im aktuellen Verzeichnis einen Dateinamen gibt, dessen Name mit beginnt -(oder eine Datei mit dem Namen !oder (...), wird dies von als Option oder Prädikat interpretiert find.

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 prüfen, ob jedes Muster mit mindestens einer Datei übereinstimmt, und diese auslassen, wenn dies nicht der Fall ist. Dies ist möglich, aber sehr umständlich. Der letzte Punkt ist ein Stopper. Er find *eignet sich möglicherweise für die schnelle Verwendung über die Befehlszeile, sollte jedoch nicht in einem Skript verwendet werden.

Ein alternativer Ansatz besteht darin, die rekursive Globbing-Funktion der Shell zu verwenden, z. B.

printf '%s\n' **/*.h

shopt -s globstarDies muss in Bash und in KSH93 aktiviert werden set -o globstarund existiert nicht in einer einfachen POSIX-Shell wie Dash. Dot-Dateien werden standardmäßig nicht durchlaufen. Um sie einzuschließen, stellen Sie zunächst sicher, dass Globbing Dot-Dateien in shopt -s dotglobBash oder FIGNORE='@(.|..)'KSH93 nicht ignoriert. Wenn es keine Übereinstimmungen gibt, druckt dieser Befehl das Muster. Führen Sie ihn shopt -s nullglobin Bash aus, um stattdessen eine leere Zeile zu drucken, und verwenden Sie das Muster ~(N)**/*.hin KSH.

In zsh ist rekursives Globbing standardmäßig aktiviert. Verwenden Sie den Glob-Qualifizierer, Dum Punktdateien einzuschließen und eine leere Zeile zu drucken, wenn es keine Übereinstimmungen gibt (standardmäßig gibt zsh einen Fehler aus, wenn ein Muster mit keiner Datei übereinstimmt). Sie können wie oben Nverwenden oderprintf

print -rl -- **/*.h(DN)

Antwort4

Um die Ausgabe von find ohne das führende } zu erhalten, stehen Ihnen verschiedene Tricks zur Verfügung ./:

  1. Verwenden Sie die Option „find“ -printfund weisen Sie es an, nur zu drucken %f. Siehe man find:

    %f Dateiname, wobei alle führenden Verzeichnisse entfernt wurden (nur das letzte Element).

    Zum Beispiel:

    find . -name "*.h" -printf "%f\n"
    
  2. Analysieren Sie die Ausgabe:

    find . -name "*.h" | sed 's#^./##'
    
  3. @Anthons Trick

    find * -name "*.h"
    

Was Ihre andere Frage betrifft, bezeichnet die leere Zeichenfolge niemals das aktuelle Verzeichnis. 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.

verwandte Informationen