Wie bekomme ich nur mit Linux 'find' den Dateinamen?

266

Ich verwende find für alle Dateien im Verzeichnis, daher erhalte ich eine Liste der Pfade. Ich brauche jedoch nur Dateinamen. dh ich bekomme ./dir1/dir2/file.txtund ich will bekommenfile.txt

Marverix
quelle

Antworten:

359

In GNU können findSie dafür -printfParameter verwenden, z.

find /dir1 -type f -printf "%f\n"
SiegeX
quelle
9
Klar die Antwort , aber es fehlen Details.
Jason McCreary
25
Dies ist nur für GNU
Osama F Elias
3
Dies funktioniert nicht für mich, wenn ich mehrere Dateitypen verwende (-o Schalter)
Urchin
9
find: -printf: unbekannter Primär- oder Operator
Holms
@ Urchin Kein Grund, es sollte nicht sein, solange Sie die richtige Logik haben (dh -oeine niedrigere Priorität als impliziert hat -a, so dass Sie häufig Ihre -oArgumente gruppieren möchten )
Setzen Sie Monica bitte
157

Wenn Ihr Fund keine Option -printf hat, können Sie auch den Basisnamen verwenden:

find ./dir1 -type f -exec basename {} \;
Kambus
quelle
17
Das Semikolon zu zitieren ist ein weiterer Weg, um zu unterscheiden:... {} ';'
David Kammern
28

Verwenden Sie -execdirdiese Option, in der die aktuelle Datei automatisch gespeichert ist {}, zum Beispiel:

find . -type f -execdir echo '{}' ';'

Sie können auch $PWDanstelle von verwenden .(auf einigen Systemen wird vorne kein zusätzlicher Punkt erzeugt).

Wenn Sie noch einen zusätzlichen Punkt haben, können Sie alternativ Folgendes ausführen:

find . -type f -execdir basename '{}' ';'

-execdir utility [argument ...] ;

Die -execdirPrimärdatenbank ist mit der -execPrimärdatenbank identisch, mit der Ausnahme, dass das Dienstprogramm aus dem Verzeichnis ausgeführt wird, in dem sich die aktuelle Datei befindet .

Bei Verwendung +anstelle von ;wird dann {}bei jedem Aufruf des Dienstprogramms durch so viele Pfadnamen wie möglich ersetzt. Mit anderen Worten, es werden alle Dateinamen in einer Zeile gedruckt.

Kenorb
quelle
Ich bekomme ./filenamestatt filename. Abhängig von Ihren Bedürfnissen kann es in Ordnung sein oder auch nicht.
user276648
@ user276648 Versuchen Sie es mit $PWDstatt ..
Kenorb
24

Wenn Sie GNU verwenden, finden Sie

find . -type f -printf "%f\n"

Oder Sie können eine Programmiersprache wie Ruby (1.9+) verwenden.

$ ruby -e 'Dir["**/*"].each{|x| puts File.basename(x)}'

Wenn Sie Lust auf eine Bash-Lösung (mindestens 4) haben

shopt -s globstar
for file in **; do echo ${file##*/}; done
kurumi
quelle
Ich wurde von Ihrer Antwort inspiriert, um sleske zu helfen: serverfault.com/a/745968/329412
Nolwennig
11

Wenn Sie eine Aktion nur für den Dateinamen ausführen möchten, basenamekann die Verwendung schwierig sein.

Zum Beispiel dies:

find ~/clang+llvm-3.3/bin/ -type f -exec echo basename {} \; 

wird nur den Basisnamen wiedergeben /my/found/path. Nicht das, was wir wollen, wenn wir den Dateinamen ausführen wollen.

Aber das kannst du dann xargs die Ausgabe. Zum Beispiel, um die Dateien in einem Verzeichnis basierend auf Namen in einem anderen Verzeichnis zu beenden:

cd dirIwantToRMin;
find ~/clang+llvm-3.3/bin/ -type f -exec basename {} \; | xargs rm
j03m
quelle
nicht echo -find ~/clang+llvm-3.3/bin/ -type f -exec basename {} \;
commonpike
7

Auf Mac (BSD find) verwenden:

find /dir1 -type f -exec basename {} \;
Spektrum
quelle
1

-execund -execdirsind langsam, xargsist König.

$ alias f='time find /Applications -name "*.app" -type d -maxdepth 5'; \
f -exec basename {} \; | wc -l; \
f -execdir echo {} \; | wc -l; \
f -print0 | xargs -0 -n1 basename | wc -l; \
f -print0 | xargs -0 -n1 -P 8 basename | wc -l; \
f -print0 | xargs -0 basename | wc -l

     139
    0m01.17s real     0m00.20s user     0m00.93s system
     139
    0m01.16s real     0m00.20s user     0m00.92s system
     139
    0m01.05s real     0m00.17s user     0m00.85s system
     139
    0m00.93s real     0m00.17s user     0m00.85s system
     139
    0m00.88s real     0m00.12s user     0m00.75s system

xargsDie Parallelität hilft auch.

Lustigerweise kann ich den letzten Fall von xargsohne nicht erklären-n1 . Es gibt das richtige Ergebnis und es ist das schnellste¯\_(ツ)_/¯

( basenameDauert nur 1 Pfad Argument aber xargs, ohne sie alle (tatsächlich 5000) senden -n1. Nicht die Arbeit an Linux und openbsd, nur macOS ...)

Einige größere Zahlen von einem Linux-System, um zu sehen, wie -execdirhilft, aber immer noch viel langsamer als eine Parallele xargs:

$ alias f='time find /usr/ -maxdepth 5 -type d'
$ f -exec basename {} \; | wc -l; \
f -execdir echo {} \; | wc -l; \
f -print0 | xargs -0 -n1 basename | wc -l; \
f -print0 | xargs -0 -n1 -P 8 basename | wc -l

2358
    3.63s real     0.10s user     0.41s system
2358
    1.53s real     0.05s user     0.31s system
2358
    1.30s real     0.03s user     0.21s system
2358
    0.41s real     0.03s user     0.25s system
minusf
quelle
1
nur ein weiterer Datenpunkt: auf openbsd in einem langen Lauf findist es , -execdirdass die schnellst als die Schaffung neue Verfahren wird ein relativ teurer Betrieb ist.
Minus vom
1

Ehrlich gesagt basenameund dirnameLösungen sind einfacher, aber Sie können dies auch überprüfen:

find . -type f | grep -oP "[^/]*$"

oder

find . -type f | rev | cut -d '/' -f1 | rev

oder

find . -type f | sed "s/.*\///"
Freeman
quelle
0

Wie andere bereits betont haben, können Sie findund kombinieren. basenameStandardmäßig wird das basenameProgramm jedoch jeweils nur auf einem Pfad ausgeführt. Daher muss die ausführbare Datei für jeden Pfad einmal gestartet werden (entweder find ... -execoder find ... | xargs -n 1), was möglicherweise langsam ist.

Wenn Sie die -aOption aktivieren basename, können mehrere Dateinamen in einem einzigen Aufruf akzeptiert werden. Dies bedeutet, dass Sie die Pfade xargsohne die Option -n 1zu einer weitaus geringeren Anzahl von Aufrufen von gruppieren können basename, was effizienter sein sollte.

Beispiel:

find /dir1 -type f -print0 | xargs -0 basename -a

Hier habe ich das -print0und aufgenommen-0 (das zusammen verwendet werden sollte) eingefügt, um mit Leerzeichen in den Namen von Dateien und Verzeichnissen fertig zu werden.

Hier ist ein Zeitvergleich zwischen xargs basename -aund xargs -n1 basenameVersionen. (Um einen Vergleich mit ähnlichen Daten zu ermöglichen, werden die hier angegebenen Timings nach einem ersten Dummy-Lauf angezeigt, sodass beide ausgeführt werden, nachdem die Dateimetadaten bereits in den E / A-Cache kopiert wurden.) Ich habe die Ausgabe an weitergeleitet cksumIn beiden Fällen nur, um zu demonstrieren, dass die Ausgabe unabhängig von der verwendeten Methode ist.

$ time sh -c 'find /usr/lib -type f -print0 | xargs -0 basename -a | cksum'
2532163462 546663

real    0m0.063s
user    0m0.058s
sys 0m0.040s

$ time sh -c 'find /usr/lib -type f -print0 | xargs -0 -n 1 basename | cksum' 
2532163462 546663

real    0m14.504s
user    0m12.474s
sys 0m3.109s

Wie Sie sehen, ist es wesentlich schneller, nicht basenamejedes Mal zu starten .

alaniwi
quelle
Wenn ich die Antwort von @minusf genauer lese, sehe ich, dass auf einem Mac basenamemehrere Dateinamen akzeptiert werden, ohne dass zusätzliche Befehlszeilenargumente erforderlich sind. Die Verwendung von -ahier ist unter Linux. ( basename --versionsagt es mir basename (GNU coreutils) 8.28.)
alaniwi
-3

Ich habe eine Lösung gefunden (auf der Seite makandracards), die nur den neuesten Dateinamen enthält:

ls -1tr * | tail -1

(Dank geht an Arne Hartherz)

Ich habe es benutzt für cp:

cp $(ls -1tr * | tail -1) /tmp/
Jazzinka
quelle
2
Dies beantwortet die Frage überhaupt nicht.
Kenorb