Wann bezeichnet die leere Zeichenfolge das aktuelle Verzeichnis?

8

In einem Skript findsammle ich einige Dateien im aktuellen Verzeichnis, wie in

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

Jetzt möchte ich, dass es nur foo.hohne ./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 cutoder sed?

phs
quelle
1
findMit dem Parameter -printf können Sie festlegen, wie die Ergebnisse angezeigt werden. find . -name '*.h' -printf '%P\n'entfernt das ./Präfix. Sehen Sie, was find . -name '*.h' -printtut.
Valentin Bajrami
Vielen Dank! Schade, dass meine Find-Version die -printfOption nicht unterstützt . FWIW, stringsauf meinem Fund gibt @(#)PROGRAM:find PROJECT:shell_cmds-175?!?
Phs
5
@phs Das ist die Art von Dingen, die es wichtig machen, dass Sie immer Ihr Betriebssystem erwähnen.
Terdon

Antworten:

11

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:

Ein Nullpfadname wird nicht erfolgreich aufgelöst.

Mark Plotnick
quelle
3
Eine leere Zeichenfolge in $ PATH, $ MANPATH, $ LD_LIBRARY_PATH (und anderen, aber nicht unbedingt allen $ * PATH-Zeichenfolgen) kennzeichnet das aktuelle Verzeichnis. dir/ist meistens das gleiche wiedir/.
Stéphane Chazelas
4

Sie können verwenden:

find * -name "*.h"

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

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).

Anthon
quelle
4

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

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

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 findist 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 verwenden find . …, beachten Sie jedoch, dass find *es eine Reihe von Fehlern gibt, die es im Allgemeinen ungeeignet machen:

  • . wurde weggelassen.
  • Alle .Punktdateien (Dateien, deren Name mit oder ..` beginnt ) werden weggelassen.
  • Wenn sich im aktuellen Verzeichnis ein Dateiname befindet, dessen Name mit -(oder einer Datei mit dem Namen !oder (...) beginnt , wird dieser als Option oder Prädikat von 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 ü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. So find *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

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

Dies muss von shopt -s globstarin bash und von set -o globstarin 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 mit shopt -s dotglobin bash oder FIGNORE='@(.|..)'in ksh93 nicht ignorieren . Wenn keine Übereinstimmungen vorhanden sind, gibt dieser Befehl das Muster aus. Führen Sie 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 das Glob-Qualifikationsmerkmal D, um Punktdateien einzuschließen und Neine leere Zeile zu drucken, wenn keine Übereinstimmungen vorliegen (standardmäßig gibt zsh einen Fehler aus, wenn ein Muster keiner Datei entspricht). Sie können printfwie oben oder verwenden

print -rl -- **/*.h(DN)
Gilles 'SO - hör auf böse zu sein'
quelle
Vielen Dank für all diese Tipps. Die meisten Mängel mit find *sind für mich neu und einige sind erschreckend !!
Phs
2

Ich kann mir kein Beispiel vorstellen, bei dem die leere Zeichenfolge das aktuelle Verzeichnis bezeichnet .. Sie denken vielleicht an Aufrufe wie ls, aber das liegt daran, lsdass das aktuelle Verzeichnis angenommen wird, wenn kein Parameter angegeben ist, und tatsächlich wird die leere Zeichenfolge nicht verwendet:

ulmi@silberfisch:~$ ls ""
ls: cannot access : No such file or directory
Ulrich Schwarz
quelle
3
Ein Beispiel, bei dem die leere Zeichenfolge das aktuelle Verzeichnis angibt, ist eine PATHKomponente.
DVD
0

Es gibt verschiedene Tricks, mit denen Sie die Ausgabe von find ohne den führenden erhalten können ./:

  1. Verwenden Sie die -printfOption find und weisen Sie sie an, nur zu drucken %f. Siehe man find:

    % f Dateiname, bei dem 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"
    

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.

terdon
quelle
Das setzt GNU voraus find. Oder verwenden Sie, %Pum nur die./
Stéphane Chazelas
1
find * -name '*.h'wird finden foo/.bar.h, aber nicht .bar.him aktuellen Verzeichnis.
Stéphane Chazelas
0

Wenn sich die Dateien im aktuellen Verzeichnis befinden, können Sie einfach Folgendes verwenden ls:

$ ls *.sh

Wenn Sie die Dateien auch in Unterverzeichnissen haben möchten und nur den Dateinamen benötigen, können Sie Folgendes tun:

$ find . -name '*.h' -exec basename {} \;

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).

Karl
quelle
das OP wollte nur ./ entfernt werden, während Ihre findLösung alle Verzeichniskomponenten entfernt
artm