Ausschließen von Verzeichnissen bei der Suche nach Orten

12

Eine Suche mit locatefindet Pfade im Dateisystem.
Oft wissen Sie von vornherein, dass Sie entweder nur an Dateien oder nur an Verzeichnissen interessiert sind.
Eine Suche nach Orten liefert oft viele Ergebnisse. Es wäre nützlich, nur einen der Typen in das Ergebnis aufzunehmen, da dies die Ausgabe verkürzt.

Es gibt jedoch ein interessanteres Argument, Dateien oder Verzeichnisse wegzulassen: Die Liste der Ergebnispfade kann nicht nur theoretisch mehrdeutig sein.

Das folgende Beispiel ist ein realer Fall und nicht ungewöhnlich:

$ locate --regex --basename "xfce4-keyboard-overlay$"
/usr/local/bin/xfce4-keyboard-overlay
/usr/local/share/xfce4-keyboard-overlay

Ok, wir haben was gefunden! Aber ... Dateien oder Verzeichnisse?

$ file /usr/local/bin/xfce4-keyboard-overlay 
/usr/local/bin/xfce4-keyboard-overlay:   bash script

Das ist also eine Datei ...

$ file /usr/local/share/xfce4-keyboard-overlay
/usr/local/share/xfce4-keyboard-overlay: directory

während die zweite nicht ist.

Diese Mehrdeutigkeit erschwert das Lesen langer Pfadlisten locate. Es wäre also sehr hilfreich, Verzeichnisse herauszufiltern, beispielsweise mit einer Kommandozeilenoption für .

Gibt es so etwas? Auch wenn der Filter für Verzeichnisse von locate getrennt ist?

Zumindest könnte man ein Skript verwenden, um alle zu überprüfenden Dateinamen zu iterieren - was langsam sein kann.

Volker Siegel
quelle

Antworten:

3

Mit zsh:

print -rl ${(0)^"$(locate -0 ...)"}(N.)

(0)ist ein Parametererweiterungs-Flag, das sich auf NUL-Zeichen aufteilt (wie wir es verwenden locate -0), kurz für (ps:\0:).

Mit ^, anstelle der Zugabe (N.)am Ende des Arrays, fügen wir es jedes Element.

(N.)ist ein Glob-Qualifikationsmerkmal, .um nur reguläre Dateien abzugleichen, Num das Element zu entfernen, wenn es nicht übereinstimmt (es existiert nicht oder ist keine reguläre Datei, oder wir können es nicht überprüfen). Sie können ^/anstelle von auch verwenden ., um Nicht-Verzeichnisse anstelle von nur regulären Dateien abzugleichen.

print -rldruckt jedes Argument roh auf einer separaten Zeile .

Sie können beliebige zshGlob-Qualifikationsmerkmale verwenden, aber beachten Sie, dass die Reihenfolge keine Auswirkungen hat, da wir hier ein Glob pro Datei erweitern, sodass für jede Datei nur eine zu sortierende Datei vorhanden ist.

(Beachten Sie, dass dies möglicherweise fehlschlägt, wenn die letzte von gemeldete Datei mit locateZeilenumbrüchen endet (ein in allen Shells vorhandener Fehler bei der Befehlsersetzung).)

Stéphane Chazelas
quelle
3

Dies ist ungefähr so ​​inelegant wie die anderen Antworten, aber vielleicht weniger ineffizient:

locate --regex --basename "xfce4-keyboard-overlay$" | 
        while IFS= read -r f; do [ -f "$f" ] && printf "%s\n" "$f"; done

(zweizeilig zur besseren Lesbarkeit). Die obigen Anweisungen behandeln Namen, die Leerzeichen enthalten. Das IFS=scheint notwendig zu sein, um Namen mit nachgestellten Leerzeichen zu behandeln, und natürlich -rkönnen Sie mit Backslashes umgehen.

locateWenn Pfadnamen mit Zeilenumbrüchen vorhanden sind, kann der Ansatz "Lasst uns in etwas hineinpfeifen" zum Scheitern verurteilt sein.


Für weitere Informationen IFSlesen Sie sh(1)oder bash(1) (indem Sie man shoder man bashin ein * nix-System eingeben und / oder hier , hier , hier und / oder hier lesen ). Lesen Sie dann Grundlegendes zu IFS und Bash: Lesen Sie Zeile für Zeile mit IFS auf Stack Exchange (konzentrieren Sie sich auf die Antworten mit mehr als 5 Stimmen), und wenn Sie immer noch nicht genug haben, lesen Sie IFS in Gregs Wiki- und IFS-Suchergebnissen im Bash Hackers Wiki (nicht bei Stack Exchange).

G-Man sagt, "Monica wiedereinstellen"
quelle
Können Sie einige Informationen hinzufügen, was das "IFS =" nach Ihrer whileAnweisung bewirkt?
Robert
Ich habe es getan.
G-Man sagt, dass Monica am
Backslashes sind bei vielen Echo-Implementierungen immer noch ein Problem. Sie sollten printffür beliebige Daten verwenden .
Stéphane Chazelas
Möglicherweise gibt es eine Lösung für Ihr Newlines-Problem, indem Sie den Parameter "--null" verwenden locateund readwie hier vorgeschlagen erweitern. transnum.blogspot.ie/2008/11/…
robert
@ StéphaneChazelas: Guter Punkt. Fest.
G-Man sagt, dass Monica am
2
locate --null --regex --basename "xfce4-keyboard-overlay$" |
  xargs -r0 sh -c 'find "$@" -prune ! -type d' sh
FloHimself
quelle
Eigentlich ist es noch dreckiger als es aussieht ... aber eine gute Inspiration. Stellen wir uns vor, es ist Pseudocode, dann ist es hilfreich :)
Volker Siegel
1
@ Volker: Ich stimme zu, dass es schlecht ist: Es wird in Ihrem Beispiel /usr/local/share/xfce4-keyboard-overlay alle Unterverzeichnisse auflisten . Hinzufügen -maxdepth 0hilft.
G-Man sagt, dass Monica am
Es geht noch besser ...: D locate --regex --basename "xfce4-keyboard-overlay$" | xargs -I % sh -c "test -d % && echo %"
FloHimself
1
Die Verwendung von xargswith findwar eine gute Idee. Ich habe es bearbeitet, um es stabiler zu machen. Ich hoffe es macht dir nichts aus.
Stéphane Chazelas
1

xargswird für jede Zeile Befehl wiederholen , wenn Sie angeben , -L 1oder -iParameter.

Siehe hier

$ locate --regex --basename "xfce4-keyboard-overlay$" | xargs -i bash -c '(test -d "{}" && echo "{}")'

Zugegeben, es wird eine neue Shell für jede Datei erstellt, aber es hat den Vorteil, dass es schön und kompakt ist.

EDIT: Ich war mit dieser Antwort nicht ganz zufrieden, da für jede Datei eine neue Shell gestartet wurde. Dies sollte nur zwei Prozesse haben:

$ locate --regex --basename "xfce4-keyboard-overlay$" | xargs -i echo 'test -d "{}" && echo "{}"' | bash

Natürlich wäre es schön, wenn wir es vermeiden könnten, einen Dolmetscher aus dem Weg xargszu räumen , aber seine Fähigkeit, Befehle zu verketten , scheint eingeschränkt zu sein.

Robert
quelle
3
Dieser hat gerade meinen Computer neu gestartet (es gab eine Datei mit dem Namen /home/evil/$(reboot)/xfce4-keyboard-overlayund ich habe sie dummerweise als ausgeführt root).
Stéphane Chazelas
2
@ StéphaneChazelas +1 für den Mut, "Zufallscode aus dem Internet" als root auszuführen;) (scnr)
Volker Siegel
0

Meine zwei Cent:

while IFS= read i; \
do \
  if [ -f "$i" ]; \
  then \
    echo "$i"; \
  fi; \
done < <(locate --regex --basename "xfce4-keyboard-overlay$")

Dies ist mehr oder weniger die Art und Weise, wie G-Man dies mit Prozesssubstitution kombiniert.

Tristan Storch
quelle
Tatsächlich ist dies mehr oder weniger so, wie ich es getan habe, kombiniert mit einer Prozessersetzung, abzüglich der Fähigkeit, Dateinamen zu behandeln, die Backslashes enthalten oder nachgestellte Leerzeichen enthalten. Beachten Sie auch, dass der Fragentitel "Verzeichnisse ausschließen" und diese Antwort lautet enthält nur Verzeichnisse.
G-Man sagt, dass Monica am
Es tut uns leid. Mein Fehler. Korrigiert
Tristan Storch
-1

Was ist, wenn Sie locatemit fileund kombinieren grep? ...

$ for f in `locate --regex --basename "xfce4-keyboard-overlay$"`; do file $f; done | grep -vi directory
petry
quelle
Ich habe nicht getestet, aber ich denke, das kann langsam sein, weil es einen Prozess filefür jeden einzelnen Pfad erstellt. Beachten Sie, dass es häufig viele Ergebniszeilen für locate gibt. Mein aktueller Test ist die Suche nach "gnome", mit etwa 73000 Pfaden zum Testen.
Volker Siegel
2
@ Volker: Es ist schlimmer als das: Für jede $fDatei öffnet das fileProgramm diese Datei und liest daraus . Dies ist extrem teuer, wenn Sie nur a . ………… Außerdem führt dies zu falschen Ergebnissen für Dateien, deren Name "directory" enthält (z. B. "phone_directory"). …………… (Außerdem kann die Syntax keine Namen verarbeiten, die Leerzeichen enthalten.)stat()for f in `…`; do …
G-Man sagt, dass Monica am