Wie verwende ich den Befehl find, um Dateinamen aufzulisten, aber keinen Pfad?
Ich habe einige Antworten online überprüft, aber sie funktionieren nicht, wenn -name
der Befehl einen Parameter enthält (möglicherweise mache ich etwas falsch, nicht sicher). Was ich tun möchte, ist, nur Dateinamen aufzulisten (nicht das Verzeichnis / den Pfad), und um es noch komplizierter zu machen, habe ich denselben Dateinamen mit unterschiedlicher Erweiterung, daher muss ich das Ergebnis mit -name
und filtern. -o
Das Problem sind die Vorschläge, die ich mache Säge verwenden entweder -printf
oder basename
, und die funktionieren nicht gut, wenn es gibt -name *.txt -o name *.pdf
. Gibt es eine andere Möglichkeit, dieses Problem zu lösen?
Beispiel meiner Dateien:
/dir1/dir2/dir3/dir4/
/file1.txt
/file1.pdf
/file1.ods ...etc.
Ich bin nur daran interessiert, einen oder mehrere Dateitypen pro Eintrag aufzulisten (indem -name
ich meine Ergebnisse filtere.
Ist es möglich, dies mit nur einem find
Befehl auszuführen, oder muss ich dies in zwei Schritten tun (Speichern des Ergebnisses in einer temporären Datei, dann Filtern / Entfernen des Verzeichnisses aus der temporären Datei)? Das Ergebnis, das ich erreichen möchte, ist eine Textdatei mit dem Dateinamen pro Zeile:
file1.txt
file1.pdf
file1.ods
file2.txt
file2.pdf
etc.
Der Befehl, den ich verwendet habe, ist
find /dir1/dir2/dir3/dir4/ -type f -name '*.txt' -o -name '*.pdf' -o -name '*.ods' -printf "%f\n"
Ich benutze GNU find.
Aktualisieren
Es stellte sich heraus, dass ich setzen muss \(...\)
, wie von αғsнιη erinnert
Antworten
Wenn Sie mehrere Ausdrücke im logischen ODER-Modus verwenden -o
, müssen Sie sie alle einschließen \( ... \)
. Andernfalls wird das Ergebnis des letzten Ausdrucks immer berücksichtigt. und -printf '%f\n'
wird verwendet, um den Dateinamen nur zu drucken, wenn führende Verzeichnisse entfernt wurden.
find . -type f \( -name '*.pdf' -o -name '*.txt' \) -printf '%f\n'
Siehe man findunter OPERATORS Abschnitt über ( expr )
:
BETREIBER
...(Ausdruck)
Vorrang erzwingen . Da Klammern für die Shell etwas Besonderes sind, müssen Sie sie normalerweise in Anführungszeichen setzen.
Sie können also entweder \( ... \)
oder '(' ... ')'
(Leerzeichen nach und vor Klammern sind wichtig) verwenden, um ihnen zu entkommen.
Wie bereits erwähnt, müssen Sie Ihre -name
Tests in Klammern gruppieren . Diese Klammern müssen \( ... \)
als z. B. '(' ... ')'
zum Schutz vor der Shell (wo sie sonst eine Subshell bezeichnen würden) maskiert oder zitiert werden .
Verwenden von Standard find
und Vereinfachen der Handhabung der Dateinamensuffixe:
set -- txt pdf ods
for suffix do
set -- "$@" -o -name "*.$suffix"
shift
done
shift
find /dir1/dir2/dir3/dir4 \( "$@" \) -type f -exec basename {} \;
Dies setzt zuerst die Positionsparameter auf die Liste der Dateinamensuffixe, die wir sehen möchten (dies könnte auch von der Befehlszeile übernommen werden, wenn dies ein Skript ist), und erstellt die ODER-Liste der -name
Tests. Dies wird dann in der find
Befehlszeile verwendet.
Der find
Befehl selbst ruft dann das basename
Dienstprogramm für jeden gefundenen Namen auf.
Alternativ könnten wir stattdessen eine Parametersubstitution verwenden, um alle bis auf die Dateinamenkomponente jedes Pfadnamens zu entfernen. Dazu müssen wir die Pfadnamen jedoch an ein Inline- sh -c
Skript übergeben:
# [...] as before [...]
find /dir1/dir2/dir3/dir4 \( "$@" \) -type f -exec sh -c '
for pathname do
printf "%s\n" "${pathname##*/}"
done' sh {} +
Ich würde erwarten, dass dies schneller abläuft als die Variation mit basename
, zumindest wenn viele Dateinamen zu verarbeiten sind, obwohl Sie mit GNU basename
von coreutils Folgendes verwenden könnten -a
oder --multiple
mögen:
find /dir1/dir2/dir3/dir4 \( "$@" \) -type f -exec basename -a {} +
Dies würde basename
so wenig wie möglich aufrufen . Wenn Sie jedoch Coreutils installiert haben, haben Sie möglicherweise auch Findutils mit GNU find
und können -printf '%f\n'
das Gleiche tun.