Find
find ist ein Kommandozeilenprogramm für die Dateisuche. Dabei kann man auf vielfältige Weise die Suche filtern, z.B. nach Dateiname, -alter, -größe und die Suchergebnisse weiterverarbeiten und/oder formatiert ausgeben.
Beschreibung
Installation
Syntax
Parameter
Optionen
Konfiguration
Dateien
Anwendungen
Dokumentation
Man-Pages
Info-Pages
Links
Intern
Weblinks
Kontrollfragen
Testfrage 1
Testfrage 2
Testfrage 3
Testfrage 4
Testfrage 5
= Linux- GNU Find = find [PFAD...] [AUSDRUCK...]
find ist ein äußerst mächtiges Instrument zum Suchen von Dateien.
Mit PFAD kann dem Kommando ein beliebiges Startverzeichnis für die Suche angegeben werden. Fehlt die Angabe, startet das Kommando im aktuellen Verzeichnis.
Allerdings ist der Verzicht auf die Pfadangabe nur bei der GNU-Version (also unter Linux) des Kommandos gestattet, deswegen empfiehlt es sich, die Angabe immer vorzunehmen.
find durchsucht in der Voreinstellung rekursiv alle Unterverzeichnisse.
Deshalb lassen sich die Anzahl der Ebenen (Option --maxdepth Anzahl) beschränken.
»Anzahl=1« bedeutet dabei die Suche einzig im angegebenen Verzeichnis.
Die gebräuchlichste Methode wird die Suche nach Dateien mit einem bestimmten Namen sein.
Die Option -name Dateiname ermöglicht dies. Dateiname kann dabei die Shell-Metazeichen zur Dateinamenserweiterung enthalten, in dem Falle muss der Name vor der Auswertung durch die Shell geschützt werden, indem er in Anführungsstriche (doppelt/einfach) eingeschlossen wird. find /usr -maxdepth 3 -name "XF*" /usr/X11R6/bin/XF86_Mach64 /usr/X11R6/bin/XF86Setup /usr/X11R6/bin/XF86_VGA16 /usr/X11R6/bin/XFCom_Cyrix /usr/X11R6/bin/XFCom_Matrox /usr/X11R6/bin/XFCom_Trident
Dateien verfügen über viele Eigenschaften. Sie haben einen Besitzer, Rechte, mehrere Zeitstempel, einen Typ, usw. Für jede dieser Eigenschaften existieren Optionen, mit denen sich gezielt Dateien bestimmter Charakteristik herausfischen lassen.
Da unter unixoiden Systemen der Leitsatz "Alles ist eine Datei" gilt, werden auch Verzeichnisse gefunden. Eine Alternative zu find (mit Vor- und Nachteilen) bietet der Befehl locate.
Implementierungen
Nicht alle Implementierungen von find sind gleich. Der Befehl find ist unter Linux etwas anders als auf BSDs, Solaris oder Mac OS X. Ein Großteil der Syntax von find ist gleich und kann auf allen Systemen verwendet werden, aber nicht vollständig.
Bei Schwierigkeiten prüfen Sie die mab-Page Ihres Systems.
Version of find: $ find --version find (GNU findutils) 4.4.2
Alternativen
find ist fast immer das Mittel der Wahl, wenn es darum geht, auch Unterverzeichnisse zu durchsuchen.
locate
Wenn man den Dateinamen genau kennt, kann locate eine bessere Wahl sein. locate arbeitet allerdings mit einem Index, der 1x täglich aktualisiert wird, und findet daher ganz frische Dateien nicht.
Suche nach Programmen
wird man whereis benutzen.
Im aktuellen Verzeichnis, ohne Unterverzeichnisse
zu berücksichtigen, kommt man mit automatischer Vervollständigung und den Jokerzeichen * und ? oft weiter, wenn man Namensbestandteile kennt.
Manche Programme bieten auch von sich aus an, Unterverzeichnisse zu berücksichtigen, siehe ls und Shell/grep.
Bedingungen verknüpfen
find hält logische Operatoren bereit, um verschiedene Kriterien miteinander zu verknüpfen.
Tests kombinieren
- <c1> -a <c2>: Wahr, wenn beide, <c1> und <c2>, wahr sind. -a wird bereits implizit aktiviert, daher brauchen Sie nur <c1> <c2> <c3>... eingeben, wenn Sie alle Tests <c1>, <c2>,... durchführen möchten.
- <c1> -o <c2>: Wahr, wenn entweder <c1>, <c2> oder beide wahr sind. -o hat eine geringere Wertigkeit als -a. Wenn Sie also Dateien möchten, die Kriterium <c1> oder <c2> und Kriterium <c3> entsprechen, müssen Sie Klammern verwenden: ( <c1>-o <c2> ) -a <c3>. Damit die hier verwendeten Klammern nicht von Ihrer Shell interpretiert werden, müssen Sie sie durch Voranstellen eines Rückstriches (\) schützen.
- -not <c1>: Invertiert den Test <c1>. Also ist -not <c1> wahr, wenn <c1> falsch ist.
Operatoren
Die Optionen, Tests und Aktionen können mit Operatoren verknüpft werden.
Die Bearbeitung erfolgt von links nach rechts.
( Ausdruck ) | die Klammern fassen den Ausdruck zu einer Operation zusammen |
! Ausdruck | ist wahr, wenn der Ausdruck falsch ist |
-not Ausdruck | ist ebenfalls wahr, wenn der Ausdruck falsch ist |
Ausdruck1 Ausdruck2 | UND Verknüpfung; wenn Ausdruck1 wahr ist, wird Ausdruck2 bewertet (ausgeführt) |
Ausdruck1 -a Ausdruck2 | auch eine UND Verknüpfung |
Ausdruck1 -and Ausdruck2 | auch eine UND Verknüpfung |
Ausdruck1 -o Ausdruck2 | ODER Verknüpfung; Ausdruck2 wird bewertet (ausgeführt), wenn Ausdruck1 falsch ist |
Ausdruck1 -or Ausdruck2 | auch eine ODER Verknüpfung |
Ausdruck1 , Ausdruck2 | Liste; beide Ausdrücke werden immer bewertet (ausgeführt); der Wahrheitswert des gesamten Ausdrucks entspricht dem von Ausdruck2 |
Und-Kombination
Treffer müssen alle Kriterien erfüllen find -mindepth 3 -maxdepth 5
Finde ab Unterverzeichnis(se) 3 (mindepth 3) UND bis Unterverzeichnis(se) 5 (-maxdepth 5).
Weiters Beispiel der UND-Kombination: find -mindepth 3 -type f -name "*.avi" -size +5M
Beginnt die Suche ab Unterverzeichnis(se) 3 (-mindepth 3), UND findet nur gewöhnliche Dateien (‑type f), die die Endung .avi besitzen UND mindestens 5 MB groß sind (-size +5M).
=== Oder-Kombination === find -name "susi.*" -or -name "susanne.*"
Sucht alle Dateien die mit "susi." ODER "susanne." beginnen.
ODER bzw. NICHT-Kombination
Man kann die Suchoptionen aber auch per ODER bzw. NICHT verknüpfen:
Negation find ! -name "*.avi" -not -name "*.mp*g"
Sucht Dateien die von der Dateiendung weder avi, noch mpg oder mpeg sind. Ausrufezeichen und -not sind gleichbedeutend.
Klammerung
Bei umfangreichen Kombinationen kann eine Klammerung erforderlich sein, um das gewünschte Resultat zu erhalten.
==== ohne Klammern ==== find -name "susi.*" -or -name "susanne.*" -name "*.txt"
Ohne Klammern wird erst die UND-Verbindung gebildet, also "susanne.*" und "*.txt", danach erst ODER "susi". susi.png würde also gefunden.
==== mit Klammern ==== find \( -name "susi.*" -or -name "susanne.*" \) -name "*.txt"
Klammern müssen maskiert werden. Hier wird jetzt für alle Dateien erfordert, dass diese auf .txt enden.
Beispiel
Suche nach ausführbaren Dateien (keine Verzeichnisse), die »user« gehören find . -maxdepth 1 \! -type d -a -perm +111 -a -user user ./.xinitrc ./.xsession ./dialog ./selfeditor.pl
Erläuterung* Die Suche wurde auf das aktuelle Verzeichnis beschränkt "-maxdepth 1"
- Verzeichnisse ausgeschlossen "\! -type d" (das "!" ist ein Sonderzeichen der Shell, deswegen muss die Auswertung durch die Shell verhindert werden).
- Es sollen alle Dateien gefunden werden, bei denen mindestens in einer [../../../F:%5Cebooks%5CLinux%5CEinführungen%5Clinuxfibel%5Ceigentum.htm Rechtegruppe] das x-Flag gesetzt ist "-perm +111" und die »user» gehören "-user user". Die Suchkriterien sind jeweils per UND zu verknüpfen.
- Da find rekursiv alle Unterverzeichnisse (eventuell bis zu einer bestimmten Tiefe) durchsucht, kann die Ausführung sehr langwierig werden.
Aktionen
-ls
zeigt das Verzeichnis in dem die getestete Datei gefunden wurde mit ls -dils an
-delete
Löscht die gefundenen Dateien
Achtung!
Da find auch Unterverzeichnisse durchsucht, sollte mit dieser Option vorsichtig umgegangen werden.
Mit find gelöschte Dateien landen nicht im Papierkorb und können nicht wieder hergestellt werden. Siehe auch Die Aktion -delete steht an der falschen Stelle
Vor der Verwendung sollte ein Test ohne -delete voraus gehen, um sicher zu gehen, nicht zu viele Dateien zu löschen.
Die -delete-Option impliziert -depth, d.h. man muss zum Testen auch -depth setzen, um keine Überraschung zu erleben. Es ist auch sorgfältig darauf zu achten, an welcher Position -delete steht. find test/ -name "c*" -delete
Löscht im Verzeichnis test und dessen Unterverzeichnissen alle Dateien, die mit "c" beginnen.
Der Befehl löscht auch Verzeichnisse selbst, die mit "c" beginnen, diese jedoch nur, wenn sie leer sind, wie allgemein üblich bei Linux.
Das ist der Grund, weshalb -delete ein -depth impliziert:
Wenn erst in den Unterverzeichnissen gelöscht wird kann ein leeres Oberverzeichnis auch gelöscht werden, umgekehrt nicht.
Find und anderen Kommandos
Now that you've found the files you're looking for, what are you going to do with them?
You can use find in conjunction with xargs or the -exec option to manipulate files after you've found what you're looking for.
Say you want to change the ownership of a bunch of files from root to www-data. Finding all the files is only the first step; you also need to change the ownership.
Doing it manually from a list of files returned by find sounds like tedium, and it is. So you might want to use the -exec option to change the ownership:
find -user root -exec chown www-data {} \;
That command tells find to pass all the files it finds to the chown utility and change the ownership to www-data. Piece of cake.
Dateien bearbeiten
Schließlich wünscht man sich, mit der gefundenen Datei etwas anstellen zu können, d.h. die gefundene Datei durch ein Kommando zu schleusen.
Mit der Option -exec Kommando(s) {} ; wird in jedem Schritt die gefundene Datei der Reihe nach mit den angegebenen Kommandos bearbeitet.
Die geschweiften Klammern dienen dabei als Platzhalter, der den aktuellen Dateinamen enthält.
Das Semikolon schließt die exec-Option ab:
Suche nach leeren Dateien und zeige diese mittels "ls -l" an find ./bsp/ -empty -exec ls -l \{\} \; insgesamt 0 insgesamt 0 -rw-r--r-- 1 user users 0 Jun 16 09:30 ./bsp/lib/bla/file -rw-r--r-- 1 user users 0 Jun 16 09:30 ./bsp/lib/foo/file
Bemerkung
Die Maskierung der geschweiften Klammern und des Semikolons ist entscheidend, da diese sonst von der Shell substituiert werden (ein Semikolon wird bspw. entfernt).
Auch muss zwischen schließender Klammer und Semikolon ein Leerzeichen stehen!
-exec Kommando {} \;
Wendet auf alle gefundenen Dateien den Shellbefehl "Kommando" an. {} steht dabei als Platzhalter für die gefundenen Dateinamen.
Das Zeichen ; terminiert den von find aufzurufenden Shellbefehl, damit es nicht unbeabsichtigt von der Shell interpretiert wird muss es mit \ maskiert werden.
-exec Kommando; <<
führt das Kommando aus; die Aktion ist wahr, wenn das Kommando einen Status von Null liefert; alle auf den Kommandonamen folgenden Argumente bis zu einem Semikolon.
werden als Kommandozeilenargumente für das Kommando interpretiert.
das Semikolon kann nicht weggelassen werden, und es muss durch mindestens ein Whitespace von der letzten Option getrennt werden
die Konstruktion {} wird durch den Pfadnamen der Datei ersetzt; die Klammern und das Semikolon müssen in der Kommandozeile für find quotiert werden, damit sie nicht von der Shell bearbeitet werden
Using ‑exec Efficiently
The ‑exec action takes a command (along with its options) as an argument.
The arguments should contain {} (usually quoted), which is replaced in the command with the name of the currently found file.
The command is terminated by a semicolon, which must be quoted (“escaped”) so the shell will pass it literally to the find command.
To use a more complex action with ‑exec, you can use “complex-command” as the Unix command.
Here's a somewhat contrived example, that for each found file replaces “Mr.” with “Mr. or Ms.”, and also converts the file to uppercase: find whatever... -exec sh -c 'sed "s/Mr\./Mr. or Ms./g" "{}" \' | tr "[:lower:]" "[:upper:]" >"{}.new"' \;
The ‑exec action in find is very useful. But since it runs the command listed for every found file, it isn't very efficient.
On a large system this makes a difference! One solution is to combine find with xargs as discussed above: find whatever... | xargs command
However this approach has two limitations. Firstly not all commands accept the list of files at the end of the command. A good example is cp: find . -name \*.txt | xargs cp /tmp # This won't work!
(Note the Gnu version of cp has a non-POSIX option “‑t” for this, and Gnu xargs has options to handle this too.)
Secondly, filenames may contain spaces or newlines, which would confuse the command used with xargs. (Again Gnu tools have options for that, “find ... ‑print0 | xargs ‑0 ...”.)
There are standard POSIX (but non-obvious) solutions to both problems.
An alternate form of ‑exec ends with a plus-sign, not a semi-colon. This form collects the filenames into groups or sets, and runs the command once per set.
This is exactly what xargs does, to prevent argument lists from becoming too long for the system to handle.
In this form, the {} argument expands to the set of filenames.
For example: find / -name core -exec /bin/rm -f '{}' +
-execdir Kommando {} +
Wendet auf alle gefundenen Dateien den Shellbefehl "Kommando" an. Im Ggs. zu -exec wird das Kommando im Verzeichnis, in dem die Datei liegt, ausgeführt.
Das Plus statt des \; am Ende provoziert die parallele Ausführung der Kommandos, und kann auch bei -exec verwendet werden
Mehrere Kommandos ausführen
As for the find command, you can also just add more -exec commands in a row: find . -name "*" -exec chgrp -v new_group '{}' \; -exec chmod -v 770 '{}' \;
Note that this command is, in its result, equivalent of using chgrp -v new_group file && chmod -v 770 file
on each file.
All the find's parameters such as -name, -exec, -size and so on, are actually tests: find will continue to run them one by one as long as the entire chain so far has evaluated to true. So each consecutive -exec command is executed only if the previous ones returned true (i.e. 0 exit status of the commands). But find also understands logic operators such as or (-o) and not (!). Therefore, to use a chain of -exec tests regardless of the previous results, one would need to use something like this: find . -name "*" \( -exec chgrp -v new_group {} \; -o -exec chmod -v 770 {} \; \)
@user Unfortunately, I don't know if it is still necessary. I did some test just now and haven't come across a situation where it would change anything. I guess it's just "good practice" that will die out. – rozcietrzewiacz Aug 5 '11 at 8:04 find . -name "*" -exec sh -c 'chgrp -v new_group "$0" ; chmod -v 770 "$0"' {} \;
@Gilles: The wonders of -c's odd handling of $0 make me think this is wrong every time I glance at it, but its definitely correct. – derobert Aug 24 '12 at 16:52
I like the explicit shell being defined... – djangofan Aug 24 '12 at 19:05
-ok
Anstatt -exec kann man auch -ok verwenden. Hierbei wird jedes mal gefragt, ob man die Aktion ausführen möchte.
Meist empfiehlt sich -execdir statt -exec find test -type d -exec tar -cjf archiv.bz2 {} \;
-execdir führt das Kommando aus dem Verzeichnis heraus aus, in dem die Datei gefunden wird.
So wird also für jedes Unterverzeichnis ein archiv.bz2 vor Ort angelegt.
Mit einem einfachen -exec würde für jedes Verzeichnis ein Archiv im aktuellen Verzeichnis angelegt, d.h. das Archiv immer wieder überschrieben, so dass am Ende nur ein Archiv mit den Ergebnissen des letzten Verzeichnisses existiert.
-ok Kommando;
wie -exec, vor der Ausführung des Kommandos wird aber noch eine Bestätigung erwartet; nur eine Eingabe, die mit einem B oder einem y beginnt, führt zur Ausführung des Kommandos
-ok Kommando {} \;
Wie -exec, allerdings wird vor jeder Aktion eine Bestätigung erfragt. {} steht dabei als Platzhalter für die gefundenen Dateinamen
==== -okdir ==== find -name "*pdf" -okdir xpdf {} \;
-okdir fragt im Gegensatz zu -execdir vor jeder Datei nach, ob man wirklich die Aktion ausführen möchte.
===== Parallele Ausführung mit + ===== find -name "*pdf" -execdir md5sum {} +
Beendet man ein Kommando mit Plus + statt mit Semikolon ;, so werden mehrere, u.U. alle Funde auf einen Rutsch an das Kommando übergeben.
Dies ist dann sinnvoll, wenn das Kommando selbst mit mehreren Parametern zurechtkommt. Beispiele: find test -type f -execdir md5sum {} ";"
ergibt: md5sum a md5sum b md5sum c
Dagegen ergibt: find test -type f -execdir md5sum {} + md5sum a b c
Das + kann nur verwendet werden, wenn die geschweiften Klammern unmittelbar davor stehen.
Eine etwas heikle Angelegenheit ist das Löschen mit der Option -delete.
-okdir Kommando {} +
Wie eine Kombination von -ok und -execdir, d.h. es wird eine Bestätigung erfragt, und das Kommando wird im Fundordner ausgeführt.
Das Plus kann statt ";" verwendet werden, wenn die {} der letzte Parameter sind; dann werden mehrere Funde auf einmal an Kommando übergeben.
Ausgabe formatieren
gibt den vollständigen Pfadnamen der getesteten Datei auf die Standardausgabe
-fprint Ausgabedatei
schreibt den Pfadnamen der getesteten Datei in die Ausgabedatei; wenn die Ausgabedatei nicht existiert, wird sie erzeugt, sonst wird sie erweitert; die Standardausgabe und die Standardfehlerausgabe werden als /dev/stdout und /dev/stderr angesprochen
-fprint Datei
Gibt die gefunden Dateinamen nicht auf die Standardausgabe (Bildschirm) aus, sondern schreibt diese in die Datei "Datei"
-print0
gibt den Pfadnamen der getesteten Datei, von einem Nullbyte abgeschlossen, auf die Standardausgabe; auf diese Weise können auch Pfadnamen korrekt weiterverarbeitet werden, die ein Zeilenende enthalten
-fprint0 Ausgabedatei
schreibt den Namen der getesteten Datei in die Ausgabedatei und schließt die Ausgabe mit einem Nullbyte ab, wie -print0
-printf
Using the (non-standard) “‑printf” action instead of the default “‑print” is useful to control the output format better than you can with the ls or dir utilities.
You can use find with the ‑printf action to produce output that can easily be parsed by other utilities or imported into spreadsheets or databases. See the Gnu find man page for the dozens of possibilities with the ‑printf action.
In fact, find with ‑printf is more versatile than ls; it is the preferred tool for forensic examiners even on Windows systems, to list file information.
For example the following displays non-hidden (no leading dot) files in the current directory only (no subdirectories), with an custom output format: find . -maxdepth 1 -name '[!.]*' -printf 'Name: %16f Size: %6s\n'
“‑maxdepth” is a Gnu extension. On a modern, POSIX version of find you could use this: find . -path './*' -prune ...
On any version of find you can use this more complex (but portable) code: find . ! -name . -prune ...
which says to “prune” (don't descend into) any directories except “.”.
Note that “‑maxdepth 1” will include “.” unless you also specify “‑mindepth 1”. A portable way to include “.” is: find . \( -name . -o -prune \) ...
The “\(” and “\)” are just parenthesis used for grouping, and escaped from the shell. The “‑o” means Boolean OR. As a system administrator, you can use find to locate suspicious files (e.g., world writable files, files with no valid owner and/or group, Set files, files with unusual permissions, sizes, names, or dates). Here's a more complex example (which I saved as a shell script so I can run it often): find / -noleaf -wholename '/proc' -prune \ -o -wholename '/sys' -prune \ -o -wholename '/dev' -prune \ -o -wholename '/windows-C-Drive' -prune \ -o -perm -2 ! -type l ! -type s \ ! \( -type d -perm -1000 \) -print
This says to seach the whole system, skipping the directories /proc, /sys, /dev, and /windows-C-Drive (presumably a Windows partition on a dual-booted computer).
The Gnu ‑noleaf option tells find not to assume all remaining mounted filesystems are Unix file systems (you might have a mounted CD for instance). The “‑o” is the Boolean OR operator, and “!” is the Boolean NOT operator (applies to the following criteria).
So these criteria say to locate files that are world writable (“‑perm ‑2”, same as “‑o=w”) and NOT symlinks (“! ‑type l”) and NOT sockets (“! ‑type s”) and NOT directories with the sticky (or text) bit set (“! \( ‑type d ‑perm ‑1000 \)”).
(Symlinks, sockets, and directories with the sticky bit set, are often world-writable and generally not suspicious.)
-printf Format
gibt für die getestete Datei die Zeichenkette Format auf der Standardausgabe aus; Format kann verschiedene Sonderzeichen und Platzhalter enthalten, die von find bearbeitet werden:
\aAlarmton \bRückschritt \cAbbruch der Ausgabe \fSeitenvorschub \nZeilenvorschub \rWagenrücklauf \thorizontaler Tabulator \vvertikaler Tabulator \\der Backslash selbst
ein Backspace gefolgt von irgendeinem anderen Zeichen wird als normales Zeichen interpretiert und einfach ausgegeben
%% das Prozentzeichen selbst %a die Zeit des letzten Zugriffs auf die Datei, in dem Format der ctime Funktion %A k die Zeit des letzten Zugriffs auf die Datei, in dem von k bestimmte Format; k hat dabei das gleiche Format wie der entsprechende Parameter der strftime Funktion in C:
@ Sekunden seit dem 1.1.1970 0 Uhr GMT H Stunde (00 bis 23) I Stunde (01 bis 12) k Stunde (0 bis 23) l Stunde (1 bis 12) M Minute (00 bis 59) p PM oder AM r Zeit, 12 Stunden (hh:mm:ss: AM/PM) S Sekunden (00 bis 61) T Zeit, 24 Stunden (hh:mm:ss) X Zeit (H:M:S) Z Zeitzone, oder nichts a abgekürzter Wochentag A ausgeschriebener Wochentag b abgekürzter Monatsname B ausgeschriebener Monatsname c Datum und Zeit d Tag im Monat D Datum (mm/dd/yy) h das gleiche wie b j der Tag im Jahr m die Zahl des Monats U die Nummer der Woche, Sonntag als erster Wochentag w die Zahl des Wochentags W die Nummer der Woche, Montag als erster Wochentag x Datum (mm/dd/yy) y die letzten beiden Stellen der Jahreszahl Y die Jahreszahl
%b die Dateigröße in 512 Byte Blöcken (aufgerundet) %c das Datum der letzten Statusänderung im Format der C ctime Funktion %Ck das Datum der letzten Statusänderung im Format der BR strftime " Funktion; Parameter wie oben" %d die Höhe der Datei im Verzeichnisbaum; Null bedeutet, dass die Datei Kommandozeilenargument ist
%f der Name der getesteten Datei, ohne Verzeichnisse
%g der Gruppenname der getesteten Datei oder die Kennzahl, wenn die Gruppe nicht eingetragen ist
%G die Gruppenkennzahl
%h die Verzeichnisnamen des Pfadnamen der getesteten Datei
%H das Kommandozeilenargument (Test), mit dem die Datei gefunden wurde
%i die Nummer der Inode der getesteten Datei
%k die aufgerundete Größe der getesteten Datei in Kilobytes
%l das Objekt, auf die ein symbolischer Link zeigt; leer, wenn die getestete Datei kein symbolischer Link ist
%m die Zugriffsrechte als Oktalzahl
%n die Anzahl der harten Links auf die getestete Datei
%p der Pfadname der Datei
%P der Pfadname und das Kommandozeilenargument (Test), mit dem die Datei gefunden wurde
%s die Größe der getesteten Datei in Bytes
%t die Zeit der letzten Änderung, im ctime Format
%Tk die Zeit der letzten Änderung, im strftime Format (siehe oben)
%u der Name des Eigentümers der getesteten Datei oder die Kennzahl, wenn der Benutzer nicht eingetragen ist
%U die Benutzerkennzahl des Eigentümers der getesteten Datei
-fprintf Ausgabedatei Format
schreibt den Namen der getesteten Datei in die Ausgabedatei und benutzt dabei das Format mit Sonderzeichen wie bei printf
-prune
Wenn die Datei ein Verzeichnis ist, wird nicht in dieses hinabgestiegen. * wahr, wenn die Option -depth nicht angegeben ist
- falsch, wenn die Option -depth angegeben ist, hat keine Auswirkungen.
Beispiele
Ohne weitere Angaben gibt find die Namen der gefundenen Dateien aus: find /boot/grub/ -name "he*" /boot/grub/hexdump.mod /boot/grub/hello.mod /boot/grub/help.mod
Wie bereits gesehen kann man mit -ls eine detailliertere Ausgabe erzeugen: find /boot/grub/ -name "he*" -ls 168624 4 -rw-r--r-- 1 root root 3196 Jan 13 17:08 /boot/grub/hexdump.mod 168603 4 -rw-r--r-- 1 root root 1308 Jan 13 17:08 /boot/grub/hello.mod 168623 4 -rw-r--r-- 1 root root 2200 Jan 13 17:08 /boot/grub/help.mod
Mit -exec und dessen Varianten lassen sich beliebige Programme auf den Fundstellen ausführen.
Die Anzahl der Zeilen in Textdateien findet man mit wc -l DATEI; kombiniert mit find sieht das so aus:
find -name "*.py" -exec wc -l {} \; 10 ./x/abc.py 6 ./x/date-form.py 102 ./x/download.py
Das Kommando wc -l (Anzahl der Zeilen zählen) wird auf jede gefundene Datei angewendet.
Die geschweiften Klammern werden durch die von find gefundenen Namen ersetzt.
Am Ende muss der ganze Befehl mit einem Semikolon abgeschlossen werden.
Damit das Semikolon nicht von der Shell interpretiert wird, muss man es mit einem Backslash oder Anführungsstrichen maskieren.
Kombination mit -print find tmp -name "a" -exec touch {} \; -print ./tmp/a ./tmp/a/a ./tmp/a/a/a
touch setzt das Datum der Dateien auf den Ausführungszeitpunkt.
Da touch aber nicht den Dateinamen ausgibt sieht man nicht, welche Dateien nun betroffen waren. Daher schickt man ein -print hinterher.
Suche optimieren: xargs
Sollen viele Dateien bearbeiten werden wird für jede Datei ein eigene Befehl abgesetzt. Findet find(1) nun 2000 Dateien des Users toelpel, werden (hintereinander) 2000 Prozesse gestartet, die jeweils eine Datei löschen.
Das verlangsamt den Vorgang und erzeugt unnötig Last. rm(1) kann mehrere Dateien auf einmal löschen, ein einzelnes rm(1)-Kommando mit 2000 Argumenten wäre sinnvoller.
xargs erwartet als Parameter ein Kommando, welches dann auf Dateien einer Liste angewandt wird, die von der Standardeingabe gelesen werden.
Unter Linux/UNIX kann man fast alle Befehle auf einzelne Datei oder auch auf eine ganze Liste von Dateien anwenden.
Wenn dies nicht möglich sein sollte oder sich eine Dateiliste nicht mit Wildcards erstellen lässt, kann man das Kommando xargs verwenden.
Wenn viele Dateien (manchmal über den Verzeichnisbaum verteilt) gleichartig behandeln werden sollen, ob die Dateien nun gelöscht, der Eigentümer oder die Zugriffsrechte geändert werden soll, ist find(1) eine gute lösung. find / -user toelpel -exec rm \{\} \;
Syntax
xargs wird mit folgender Syntax in einem Terminal aufgerufen. Kommando1 | xargs [Optionen] Kommando2
Hierbei wird das "Kommando2" mit den Argumenten aufgerufen, die das "Kommando1" auf die Standardausgabe gibt.
xargs(1) hat die Funktion Kommandozeilen aus Standard-Input zu erstellen und auszuführen. find / -user toelpel -print | xargs rm
find(1) listet alle Dateien des Users toelpel auf und reicht sie über die Pipe an xargs weiter. xargs(1) nimmt die Liste von der Standardeingabe und bastelt daraus und aus dem rm(1), was ihm als Argument übergeben wurde, ein rm(1)-Kommando mit 2000 Argumenten.
Ergebnis: 3 Prozesse anstelle von 2001 Prozessen in der ersten Variante, um 2000 Dateien zu löschen.
Dateinamen mit Leerzeichen
Üblicher Weise enthält der IFS das Leerzeichen, daher bricht xargs die Dateinamen dort auseinander.
Dem ist abzuhelfen, wenn man die GNU-Version der benutzten Tools (find und xargs) verwendet. find(1) gibt man bekannt, er möge mit ASCII-NUL beendete Zeichenketten ausgeben, und xargs, er möge solche erwarten: find / -user toelpel -print0 | xargs -0 rm
Eingabedatei als Parameter
Sollen die Dateien verschoben werden, erwartet mv die Quelldateien als erstes, das Zielverzeichnis als letztes Argument. Dieses Problem löst man bei xargs(1) genauso wie bei find(1): find / -user toelpel -print0 | xargs -0 mv {} /tmp/toelpel-trash
Die Zeichenkombination "{}" zeigt dem xargs(1), an welcher Stelle er die Argumentliste für das Kommando einzufügen hat.
Optionen
-a [Datei] oder --arg-file=[Datei] | Liest die Argumente aus einer Datei anstatt von der Standardeingabe |
-0 oder --null | Argumente werden aufgrund des NULL-Zeichens getrennt anstatt des Leerzeichen. Jedes Zeichen wird wörtlich genommen. Nützlich in Kombination mit der -print0 Option von find. |
-d [Trennzeichen] oder --delimiter=[Trennzeichen] | Argumente werden aufgrund des Trennzeichens getrennt anstatt des Leerzeichens. Jedes Zeichen wird wörtlich genommen. Das Trennzeichen kann ein einzelnes Zeichen sein, ein escaptes Zeichen, ein oktaler oder ein hexadezimaler Code. Mehrere Zeichen sind nicht erlaubt. |
--help | Hilfe anzeigen. |
-i | Standardmäßig wird "{}" als Ersatz-Zeichenkette verwendet, wie bei der Aktion -exec des Befehls find. xargs fügt dann die Dateiliste an der Stelle ein, wo die Ersatz-Zeichenkette steht. Dies kommt zur Anwendung wenn der Befehl die von xargs erzeugte Dateiliste nicht am Ende erwartet. |
-p oder --interactive | Fragt vor jeder Ausführung, ob fortgefahren werden soll. |
-r oder --no-run-if-empty | Führt "Kommando2" nicht aus, wenn die Standardeingabe keine Nicht-Leerzeichen enthält. |
-n oder --max-args | Führt "Kommando2" auf jeweils eine bestimmt Anzahl von übergebenen Argumenten aus. |
-P oder --max-procs | Führt "Kommando2" mehrfach aus. Mit der Kombination mit -n kann so eine bessere Auslastung des Systems erreicht werden. |
-t oder --verbose | Gibt mehr Informationen darüber, was gemacht wird. |
=== Beispiele === find /tmp -name core -type f -print | xargs /bin/rm -f
Finde Dateien mit dem Namen core in oder unterhalb des Verzeichnisses /tmp und lösche diese.
Achtung, diese Aktion schlägt fehl falls die Dateinamen Zeilenvorschübe, einfache oder doppelte Anführungszeichen oder Leerzeichen enthalten.
Mit folgendem Befehl werden alle Dateien aus dem aktuellen Verzeichnis entfernt, die auf das Muster *.tmp passen.
Dabei werden Dateien, deren Namen Leerzeichen enthalten ebenfalls berücksichtigt: find . -name "*.tmp" -print0 | xargs -0 rm
Als nächstes eine Anwendung mit der Ersatz-Zeichenkette {}. Alle Dateien des Benutzers mit der uid 1001 werden nach /tmp/klaus/test verschoben.
Achtung!
Die Verzeichnisstruktur wird dabei nicht wieder hergestellt. Die Dateien landen wirklich alle in /tmp/klaus/test. find . -uid 1001 -print | xargs -i mv {} /tmp/klaus/test
Wenn man eine Datei software.list mit den Namen von Paketen hat, die sich im Verzeichnis ~/installation/ befindet und die wie folgt aufgebaut ist alltray audacity avidemux azureus compizconfig-settings-manager
kann man mit xargs -a ~/installation/software.list sudo apt-get install
die Pakete komfortabel alle auf einmal installieren. Dabei werden bereits installierte Pakete übersprungen.
Möchte man mehrere Dateien herunterladen, aber die Geschwindigkeit der Server lastet die eigene Internetverbindung nicht aus, so kann man mit xargs einfach mehrere Instanzen benutzen. xargs -a downloadlist -n 1 -P 4 wget
Dieser Befehl übergibt jeweils eine URL aus der Datei downloadlist an wget.
Dabei wird wget vier mal jeweils mit einer anderen Datei gestartet.
So werden vier Dateien auf einmal heruntergeladen und die Internetverbindung optimal ausgenutzt.
Weitere Möglichkeiten
This command is equivalent to using find with xargs, only a bit shorter and more efficient.
But this form of ‑exec can be combined with a shell feature to solve the other problem (names with spaces). The POSIX shell allows us to use: sh -c command-line [ command-name [ args... ] ]
We don't usually care about the command-name, so “X”, “dummy”, “”, or “'inline cmd'” is often used.
Here's an example of efficiently copying found files to /tmp, in a POSIX-compliant way. find . -name '*.txt' -type f \ -exec sh -c 'exec cp -f "$@" /tmp' X '{}' +
Obvious, simple, and readable, isn't it? Perhaps not, but worth knowing since it is safe, portable, and efficient.
Anwendungen
Beispiel 1
Wo in meinem Homeordner liegt die Datei "test.pdf"? find ~ -name test.pdf
Ihr habt vor ein paar Minuten eilig etwas gespeichert, habt aber den Namen der Datei und den Pfad sofort vergessen.
Irgendwo in eurem Homeordner, irgendein Name, vor ein paar Minuten... find ~ -type f ! -path '*/.*' -mmin -10 -ls
Die Optionen im Einzelnen:* ~ -- rekursiv in meinem Homeordner
- -type f -- eine Datei (kein Ordner...)
- ! -path '*/.*' -- ohne Dateien oder Ordner, die mit Punkt beginnen.
- -mmin -10 -- nicht älter als 10 Minuten
- -ls -- lange Anzeige (wie ls -l)
Variante: mit Dotfiles, nicht aber Ordner, die mit Punkt beginnen (also ohne .gnome/ oder .kde/ usw., aber mit .bash_history). Und zwar alle Dateien, die nicht älter als zwei Tage sind. find ~ ! -path '*/.*/*' -type f -ctime -2
Im Homeordner alle Dateien finden, deren Namen Leerzeichen enthalten.
Wieder ohne Dateien oder Ordner, die mit Punkt beginnen. find $HOME ! -path '*/.*' -type f -name '* *'* $HOME -- ist gleichbedeutend mit ~ * -name '* *' -- Name enthält mindestens ein Leerzeichen
Ganz ähnlich, aber diesmal wird nach Ordnern gesucht: find $HOME ! -path '*/.*' -type d -name '* *' -type d -- Ordner (type directory)
Im Ordner public_html alle HTML und PHP Dateien ausfindig machen, in denen ".mp3" erwähnt wird.
Nur die Dateinamen ausgeben. find ~/public_html \( -name \*.html -o -name \*.php \) | xargs grep -l '.mp3'
Zu beachten: ( ) * müssen vor der Bash versteckt, also mit Backslash escaped werden.
Andere Methode (-exec statt xargs), gleiches Ergebnis. find ~/public_html \( -name \*.html -o -name \*.php \) -exec grep -l '.mp3' '{}' \+
Zu beachten: -exec mit \+ abschließen statt mit \; beschleunigt das Abarbeiten ungemein, weil dann mehrere grep Prozesse parallell gestartet werden (vgl. xargs). Setzt eine halbwegs aktuelle Version von find voraus.
Hoppla, da sind auch Dateien mit Leerzeichen im Namen dabei? Kein Problem. find ~/public_html \( -name \*.html -o -name \*.php \) -print0 | xargs -0 grep -l '.mp3'
finds Option -print0 erzeugt die richtige Ausgabe für xargs Option -0
Anderer Einsatzzweck: Plattenplatz wird knapp, wo sind die großen Dateien?
Also z.B.: in meinem Homeordner alle Dateien, die größer als 500MB sind. find ~ -size +500M
Alle Dateien/Ordner in meinem Homeordner finden, die nicht mir gehören: find ~ ! -user $( whoami ) -ls
Alle Dateien/Ordner in meinem Homeordner, die root gehören: find ~ -user root
Alle Dateien/Ordner in meinem Homeordner, die die Rechte auf 777 gesetzt haben,
also Lese/Schreib/Ausführrechte für alle haben, lange Ausgabe wie "ls -l". find ~ -perm 777 -ls
Welche Art von Dateien (Mimetype) liegen im Ordner Documents, mit Rücksicht auf Dateien mit Leerzeichen im Namen, nur in diesem Ordner, keine Unterordner: find ~/Documents/ -maxdepth 1 -type f -print0 | xargs -0 file
Find sucht immer rekursiv, es sei denn, man schränkt mit "-maxdepth" die Tiefe ein. Die Liste ließe sich beliebig fortsetzen. Wie vielseitig find ist, zeigt sich schon an der Länge der manpage.
Find findet nach Name, Regex, Größe, Datum, Dateityp,... usw. und läßt sich mit "-exec" und der Pipe für "xargs" zu beinahe allem verwenden, was nur auf bestimmte Dateien/Ordner angewendet werden soll. Darum wird das Kommando in vielen Shellskripten verwendet.
Beispiel 2
Dateien im aktuellen Ordner und Unterordnern finden, deren Namen einem bestimmen Muster entsprechen: find . -name "*.jpg"
Dateien finden, deren Namen nicht einem bestimmten Muster entsprechen: find . \! -name "*.jpg"
Dateien finden, deren Namen einem bestimmten Muster entsprechen und die einen bestimmten Text enthalten: find . -name "*.php" -exec grep -il "suchtext" {} \;
Dateien finden, die bestimmte Datei-Endungen haben (Mit Regular-Expressions finden): find . -regex ".*(php|html|tpl)$"
Große Dateien finden (Dateien finden, die größer als ca. 500 MB sind): find . -type f -size +500000k -exec ls -lh {} \;
Dateien finden, deren Pfade einem bestimmten Pattern entsprechen: find . -path "*/.svn*"
Dateien finden, deren Pfade nicht einem bestimmten Pattern entsprechen: find . \! -path "*/.svn*"
Dateien finden, die nicht einem bestimmten User (nicht root) gehören: find . \! -user root
Dateien finden, die “oo” oder “ee” im Namen haben: find . \( -name "*oo*" -or -name "*ee*" \)
Dateien finden, die nicht “oo” oder “ee” im Namen haben: find . \! \( -name "*oo*" -or -name "*ee*" \)
Geht nicht mit find (GNU findutils) 4.4.2: find . -regex “.*(php|html|tpl)$” mkdir /tmp/tst cd /tmp/tst mkdir -p 1/2/3 mkdir -p 4/5/6 touch la.php touch 1/2/uu.tpl touch 4/lala.html touch 4/5/6/eee.php touch 4/5/oooooo' find . -regex “.*(php|html|tpl)$”
es wird nix ausgegeben :(
Beispiel 3
Angenommen, Sie möchten eine Liste der Verzeichnisse in /usr/share erhalten, dann tippen Sie: find /usr/share -type d
Angenommen, Sie haben einen HTTP-Server und alle Ihre HTML-Dateien befinden sich in /home/httpd/html, wo Sie sich auch gerade befinden. Sie möchten eine Liste aller Dateien, deren Inhalt seit einem Monat nicht verändert worden ist.
Da die Seiten von verschiedenen Schreibern stammen, enden einige auf html und einige auf htm. Sie möchten diese Dateien in das Verzeichnis /home/httpd/obsolete verknüpfen.
Geben Sie folgendes ein:[
Denken Sie daran, dass in diesem Beispiel beide Verzeichnisse auf dem selben Dateisystem sein müssen!] find \( -name "*.htm" -o -name "*.html" \) -a -ctime -30 -exec ln {} /home/httpd/obsolete \;
Gut, das hier ist etwas komplex und verlangt nach Erklärung. Das Suchkriterium ist Folgendes: \( -name "*.htm" -o -name "*.html" \) -a -ctime -30
Es findet alle Dateien, die entweder auf .htm oder auf .html enden (( -name "*.htm" -o -name "*.html" )) und (-a) die in den letzten 30 Tagen nicht modifiziert wurden (-ctime -30). Beachten Sie die Klammern, die hier notwendig sind, da -a eine höhere Wertigkeit hat.
Ließen Sie sie weg, würde das Kommando alle Dateien mit der Endung .htm finden sowie die Dateien, die auf .html enden und seit einem Monat nicht modifiziert wurden.
Beachten Sie auch, dass die Klammern vor der Shell geschützt wurden. Gäben Sie ( .. ) anstelle von \( .. \) ein, würde die Shell versuchen, diese zu interpretieren und das Kommando -name "*.htm" -o -name "*.html" in einer Sub-Shell auszuführen.
Sie können diesen Schutz übrigens auch durch Anführungszeichen erreichen.
Und schließlich das Kommando, das für jede gefundene Datei ausgeführt wird: -exec ln {} /home/httpd/obsolete \;
Auch hier müssen Sie das ; vor der Shell schützen, da diese es sonst als Kommandoseparator interpretiert und find sich beschweren wird, dass -exec ein Argument fehlt.
Ein letztes Beispiel:
Sie haben ein großes Verzeichnis mit allen möglichen Bilddateien: /shared/images.
Normalerweise benutzen Sie touch, um den Zeitstempel einer Datei namens stamp in diesem Verzeichnis aufzufrischen, um eine Zeitreferenz zu haben.
Sie wollen eine Liste aller JPEG-Dateien, die jünger als die Datei stamp sind.
Da Sie die Dateien von verschiedenen Quellen haben, haben Sie die Endungen jpg, jpeg, JPG oder JPEG. Sie möchten nicht im Verzeichnis old suchen, Sie möchten diese Liste zugeschickt bekommen und Ihr Benutzername ist john: find /shared/images -cnewer \ /shared/images/stamp \ -a -iregex ".*\.jpe?g" \ -a -not -regex ".*/old/.*" \ | mail john -s "Neue Images"
Nun wäre es nicht sehr schön, dieses Kommando regelmäßig neu eingeben zu müssen, also brauchen Sie…?
Erweiterte Anwendungen
The “‑print” action lists the names of files separated by a newline.
But it is common to pipe the output of find into xargs, which uses a space to separate file names.
This can lead to problems if any found files contain spaces in their names, as the output doesn't use any quoting.
In such cases, when the output of find contains a file name such as “foo bar” and is piped into another command, that command “sees” two file names, not one file name containing a space.
Even without using xargs, you could have a problem if the file name contains a newline character, as most utilities expect one file name per line.
In such cases, you can specify the action “‑print0” instead. This lists the found files separated not with a newline but with a null (or “”) character, which is not a legal character in Unix or Linux file names.
Of course the command that reads the output of find must be able to handle such a list of file names.
Many commands commonly used with find (such as tar or cpio) have special options to read in file names separated with NULs instead of spaces.
Instead of having find list the files, it can run some command for each file found, using the “‑exec” action. The ‑exec is followed by some shell command line, ended with a semicolon (“;”).
(The semicolon must be quoted from the shell, so find can see it!) Within that command line, the word “{}” will expand out to the name of the found file. See below for some examples.
You can use shell-style wildcards in the ‑name search argument: find . -name foo\*bar
This will search from the current directory down for foo*bar (that is, any filename that begins with foo and ends with bar).
Note that wildcards in the name argument must be quoted so the shell doesn't expand them before passing them to find.
Also, unlike regular shell wildcards, these will match leading periods in filenames. (For example “find ‑name \*.txt” would match “.foo.txt”.)
You can search for other criteria beside the name. Also you can list multiple search criteria.
When you have multiple criteria, any found files must match all listed criteria.
That is, there is an implied Boolean AND operator between the listed search criteria. find also allows OR and NOT Boolean operators, as well as grouping, to combine search criteria in powerful ways (not shown here.)
Here's an example using two search criteria: find / -type f -mtime -7 | xargs tar -rf weekly_incremental.tar gzip weekly_incremental.tar
will find any regular files (i.e., not directories or other special files) with the criterion “‑type f”, and only those modified seven or fewer days ago (“‑mtime ‑7”).
Note the use of xargs, a handy utility that coverts a stream of input (in this case the output of find) into command line arguments for the supplied command (in this case tar, used to create a backup archive).
Using the tar option “‑c” is dangerous here; xargs may invoke tar several times if there are many files found, and each “‑c” will cause tar to over-write the previous invocation.
The “‑r” option appends files to an archive. Other options such as those that would permit filenames containing spaces would be useful in a “production quality” backup script.
Another use of xargs is illustrated below. This command will efficiently remove all files named core from your system (provided you run the command as root of course): find / -name core | xargs /bin/rm -f find / -name core -exec /bin/rm -f '{}' \; # same thing find / -name core -delete # same if using Gnu find
The last two forms run the rm command once per file, and are not as efficient as the first form; but they are safer if file names contain spaces or newlines.
The first form can be made safer if rewritten to use “‑print0” instead of (the default) “‑print”. “‑exec” can be used more efficiently (see Using ‑execEfficiently below), but doing so means running the command once with many file names passed as arguments, and so has the same safety issues as with xargs.
One of my favorite of the find criteria is used to locate files modified less than 10 minutes ago. I use this right after using some system administration tool, to learn which files got changed by that tool: find / -mmin -10
This search is also useful when I've downloaded some file but can't locate it, only in that case “‑cmin” may work better.
Keep in mind neither of these criteria is standard; “‑mtime” and “‑ctime” are standard, but use days and not minutes.
Another common use is to locate all files owned by a given user (“‑user username”). This is useful when deleting user accounts.
You can also find files with various permissions set. “‑perm /permissions” means to find files with any of the specified permissions on, “‑perm -permissions” means to find files with all of the specified permissions on, and “‑perm permissions” means to find files with exactly permissions. Permissions can be specified either symbolically (preferred) or with an octal number.
The following will locate files that are writable by “others” (including symlinks, which should be writable by all): find . -perm -o=w
(Using ‑perm is more complex than this example shows.
You should check both the POSIX documentation for find (which explains how the symbolic modes work) and the Gnu findman page (which describes the Gnu extensions).
When using find to locate files for backups, it often pays to use the “‑depth” option (really a criterion that is always true), which forces the output to be depth-first—that is, files first and then the directories containing them.
This helps when the directories have restrictive permissions, and restoring the directory first could prevent the files from restoring at all (and would change the time stamp on the directory in any case).
Normally, find returns the directory first, before any of the files in that directory.
This default behavior is useful when using the “‑prune” action to prevent find from examining any files you want to ignore:
find / -name /dev -prune ...other criteria | xargs tar ...
Using just “find / ‑name /dev ‑prune | xargs tar ...” won't work as most people might expect. This says to only find files named “/dev”, and then (if a directory) don't descend into it. So you only get the single directory name “/dev”! A better plan is to use the following: find / ! -path /dev\* |xargs ...
which says find everything except pathnames that start with “/dev”. The “!” means Boolean NOT.
When specifying time with find options such as ‑mmin (minutes) or ‑mtime (24 hour periods, starting from now), you can specify a number “n” to mean exactly n, “‑n” to mean less than n, and “+n” to mean more than n.
Fractional 24-hour periods are truncated! That means that “find ‑mtime +1” says to match files modified two or more days ago.
For example: find . -mtime 0 # find files modified between now and 1 day ago # (i.e., within the past 24 hours) find . -mtime -1 # find files modified less than 1 day ago # (i.e., within the past 24 hours, as before) find . -mtime 1 # find files modified between 24 and 48 hours ago find . -mtime +1 # find files modified more than 48 hours ago find . -mmin +5 -mmin -10 # find files modified between # 6 and 9 minutes ago
=== Find und Sort === find -type f -print0 | xargs -0 stat -c "%y %n" | sort -r | head -20
==== Sort by size ==== find -type f -exec ls -ltu {} \; | sort -k 5 -n
==== Sort by access time ==== find -type f -exec ls -ltu {} \; | sort -k 6 -M
==== Search recursively by date ==== find -printf "%TY-%Tm-%Td %TT %p\n" | tail -20 | sort -n
Have you ever wanted to view a list of all files or subdirectories within a directory in Linux and order them by when they were last changed or modified? Then you have come to the right place! Here we are going to provide and explain some useful commands that when piped together will give us this result, allowing us to recursively list files and directories by date.
This is one of my favourite commands to use when trying to build a timeline of events, for instance if a server or website has been compromised and you want to see when files have been modified with malicious content. By seeing other files that were modified around the same time you can get a better idea of what took place and when, allowing you to correlate these events with your logs.
The Commands
So here are the simple commands piped together, run this within a directory and you will be provided with a list of all files and subdirectories along with the date they were last modified. The most recently changed contents will be at the bottom of the list, so after running it you’ll see the most recent changes with the older changes as you scroll up. If you have a lot of output piping the whole lot into ‘less’ may be a good idea so that you can easily scroll through. find . -printf '%T@ %t %p\n' | sort -k 1 -n | cut -d' ' -f2-
Below is an example output from running this full command. # find . -printf '%T@ %t %p\n' | sort -k 1 -n | cut -d' ' -f2- Wed Aug 26 09:25:04.0000000000 2015 ./images/1.jpg Tue Sep 1 06:27:43.0000000000 2015 ./1.JPG Sat Sep 12 12:36:51.0000000000 2015 ./directory/6.jpg Sat Sep 12 12:43:48.0166880221 2015 ./directory Mon Oct 12 05:18:21.0000000000 2015 ./images/7.jpg Sun Oct 18 08:29:46.0000000000 2015 ./8.jpg Wed Oct 21 10:50:16.0672628610 2015 ./index.html
As shown we can see the files sorted from oldest date and time modified to newest. Now let’s break down what each part is actually doing for us.
Find
First off the find command is run which finds us the list of all files and subdirectories recursively within the current working directory, as specified by the “.” after the find command. To confirm your current working directory you can run the “pwd” command. You can change the “.” to a full directory path instead to list all files and subdirectories in there instead if required, this way you don’t have to be in the directory.
The “-printf” flag is used to print the output in the format specified, in this case this is ‘%T@ %t %p\n’. The %T@ displays the epoch time, that is the amount of seconds since the 1st of January 1970, the %t shows the files last modification time, the %p displays the files name while \n is simply a new line so that each result in our output shows up on a new line which makes it easier to read and work with.
It is worth noting that you could also replace %t with %c, which will instead use the files last status change time rather than the modification time. This should show things such as permission changes which don’t actually modify the contents file but change the metadata.
The output of this find command alone looks like this. 1445424616.6726286100 Wed Oct 21 10:50:16.0672628610 2015 ./index.html
At this stage the output does not display in any sort of chronological order. We can see the output displayed as expected, the files epoch time followed by the last modification date and time, followed by the file name.
Sort
Now with this output you may have noticed that there is no order applied, this is taken care of with the sort command. The -k flag specifies a start position which in this case is 1, the first column being the epoch time.
The output with the sort is shown below, now we have the files in the same order as the output of the full command string shown previously after sorting by column 1, the epoch time. As the epoch time is all numbers, we also use -n to perform a numerical based sort. 1440581104.0000000000 Wed Aug 26 09:25:04.0000000000 2015 ./images/1.jpg 1441088863.0000000000 Tue Sep 1 06:27:43.0000000000 2015 ./1.JPG 1442061411.0000000000 Sat Sep 12 12:36:51.0000000000 2015 ./directory/6.jpg 1442061828.1668802210 Sat Sep 12 12:43:48.0166880221 2015 ./directory 1444627101.0000000000 Mon Oct 12 05:18:21.0000000000 2015 ./images/7.jpg 1445156986.0000000000 Sun Oct 18 08:29:46.0000000000 2015 ./8.jpg 1445424616.6726286100 Wed Oct 21 10:50:16.0672628610 2015 ./index.html
We can change -n to -nr which will reverse the output, resulting in the oldest modified files showing at the bottom of the output, rather than the newest.
Cut
Now that we have our sorted output we use the cut command to tidy up and print out a more specific selection. By specifying a delimiter with -d of ‘ ‘ we find the first white space which comes after the epoch time and cut everything afterwards.
At this point we now have the complete output which lists all files by date recursively from the specified directory. The epoch time provided an easy way to perform the sort, but we don’t really need to see that in the final output as it isn’t particularly human readable so it’s been removed after the sort.
Other Options
Of course you can always use the much simpler “ls -lrt” within a directory to view all files within the current working directory from oldest to newest, however this does not take into consideration subfolder contents. Even if we use the recursive option and use “ls -lRrt” we only see the files ordered based on the dates within each directory and not a combination of all subdirectories.
If you aren’t interested in the subdirectories themselves you could also add a “-type f” to the find command which will only list files, as shown below. find . -type f -printf '%T@ %t %p\n' | sort -k 1 -n | cut -d ' ' -f2-
This still lists files within subdirectories, it simply no longer also shows the subdirectory names themselves in the output.
The opposite can also be done, by using “-type d” which will display only directories and no files. find . -type d -printf '%T@ %t %p\n' | sort -k 1 -n | cut -d ' ' -f2-
Summary
By combining a few relatively simple bash commands in Linux we have been able to successfully list all files and subdirectories within a specified directory recursively by their modification or change date, in either ascending or descending order. We can also optionally specify to only view files or directories in the output.
This command is a great way of building a timeline of events as the chronological output allows us to see the order of file modifications, regardless of where the file is actually located.
Quelle
==== Weitere Lösungen ==== find -type f -printf '%T@ %p\0' | sort -zk 1nr | sed -z 's/^[^ ]* //' | tr '\0' '\n' | head -n 10 find -type f -printf '%T@ %p\n' | sort -k1 -n find -type f -print0 | xargs -0 stat -c "%y %n" | sort find -type f -print0 | xargs -0 stat -f "%m %Sm %N" | sort -rn
If you have not GNU utilities, you could use newlines as separators instead of nulls, but you'll lose support for filenames containing newlines. find -type f -printf '%T@ %p\n' | sort -k 1nr | sed 's/^[^ ]* //'
=== Compare Filetree === find directory1 -type d -printf "%P\n" | sort > file1 find directory2 -type d -printf "%P\n" | sort | diff – file1
Typische Probleme
Find ohne Ende
Wenn die Suche mit find läuft und viel zu viele Ergebnisse ausspuckt und nicht aufhören will, so bricht man find mit Strg + C ab.
Pfad muss vor Suchkriterium stehen
Wenn man den Stern * nicht maskiert kommt es oft zu folgender Meldung: find /tmp -name *sh find: Der Pfad muß vor dem Suchkriterium stehen: adhoc.sh Aufruf: find [-H] [-L] [-P] [-Olevel] [-D help|tree|search|stat|rates|opt|exec] [Pfad...] [Suchkriterium]
Mit find /tmp -name "*sh"
ist das leicht geheilt.
Seltsame Größen
Bei der Suche nach Dateigrößen kann man leicht verzweifeln, wenn man nicht dahinter kommt, dass die Vorgabemaßeinheit Blöcke zu 512 Bytes sind. find -size 200c
sucht nach Größen, die man vom Dezimalsystem her erwartet.
Seltsames Nichtfinden bei Größen wie k, M, G
Sucht man nach Dateien, die kleiner sind als 1000k, so werden Dateien bis maximal 999k gefunden: find -size -1000k
Das klingt zunächst plausibel, aber es wird keine Datei gefunden, die 999001 Bytes groß ist, denn es wird erst aufgerundet (auf 1000k) und dann verglichen (nicht kleiner als 1000k).
Krasser noch, wenn man Dateien bis 1M suchen wollte - selbst 1 Byte ist größer als die nächstkleinere Ganzzahl in dieser Maßeinheit, also größer als 0M, und wird daher nicht gefunden.
Kombination von UND und ODER
Bei der Kombination von mehreren Optionen mit UND und ODER helfen Klammern Fehler zu vermeiden.
Positionssensitiv
Bei mehreren Optionen und Ausdrücken (options und expressions) unterscheiden sich erstere von zweiteren dadurch, dass Optionen nicht mit 'ODER' gruppiert werden können - die Optionen werden immer für die ganze Suche verwendet.
Stehen die Optionen hinter Ausdrücken, so sieht das aus, als habe der User eine andere Absicht gehabt, und man bekommt eine Warnung:
Folgende Meldung erhält man, wenn man Optionen nach Argumenten benutzt. find tmp -name "a" -maxdepth 3 -mindepth 3 find: Warnung: Sie haben die Option `-maxdepth` nach dem Argument -name angegeben, aber Optionen sind nicht positionssensitiv (`-maxdepth` beeinträchtigt sowohl Tests, die vor ihr als auch nach ihr definiert sind). Diese Optionen ist vor den anderen Argumenten anzugeben.
Provozieren kann man die Warnung etwa so: find ./suchverzeichnis -maxdepth 4 -name foo -or -maxdepth 2
Xargs und Schleifen
Oft findet man Konstruktionen mit find ... xargs oder Shellschleifen die find bemühen.
Fast immer lässt sich das Problem durch eine der Aktionen (-okdir, -execdir, ...) eleganter lösen.
Aktion -delete an falscher Stelle
So löscht z.B. der folgende Aufruf den kompletten Inhalt des Ordners /home/otto/: find /home/otto/ -delete -name Cache
Common “Gotcha”
If the given expression to find does not contain any of the “action” primaries ‑exec, ‑ok, or ‑print, the given expression is effectively replaced by: find \( expression \) -print
The implied parenthesis can cause unexpected results. For example, consider these two similar commands: find -name tmp -prune -o -name \*.txt ./bin/data/secret.txt ./tmp ./missingEOL.txt ./public_html/graphics/README.txt ./datafile.txt find -name tmp -prune -o -name \*.txt -print ./bin/data/secret.txt ./missingEOL.txt ./public_html/graphics/README.txt ./datafile.txt
The lack of an action in the first command means it is equivalent to: find . \( -name tmp -prune -o -name \*.txt \) -print
This causes tmp to be included in the output. However for the second find command the normal rules of Boolean operator precedence apply, so the pruned directory does not appear in the output.
A related issue is the precedence of the Boolean operators. OR has lower precedence than AND, and NOT has the highest precedence. When in any doubt, add parenthesis to your expressions.
The find command can be amazingly useful. See the man page to learn all the criteria and actions you can use.
-regex
I'm having trouble using the regex of the find command. Probably something I don't understand about escaping on the command line.
Why are these not the same? find -regex '.*[1234567890]' find -regex '.*[[:digit:]]'
Regular expressions with character classes (e.g. [[:digit:]]) are not supported in the default regular expression syntax used by find. You need to specify a different regex type such as posix-extended in order to use them.
Take a look at GNU Find's Regular Expression documentation which shows you all the regex types and what they support.
I have some images named with generated uuid1 string. For example 81397018-b84a-11e0-9d2a-001b77dc0bed.jpg. I want to find out all these images using "find" command: find . -regex "[a-f0-9\-]\{36\}\.jpg".
But it doesn't work. Something wrong with the regex? Could someone help me with this? find . -regextype sed -regex ".*/[a-f0-9\-]\{36\}\.jpg"
Note that you need to specify .*/ in the beginning because find matches the whole path.
==== Beispiel ==== $ find . -name "*.jpg" ./foo-111.jpg ./test/81397018-b84a-11e0-9d2a-001b77dc0bed.jpg ./81397018-b84a-11e0-9d2a-001b77dc0bed.jpg $ find . -regextype sed -regex ".*/[a-f0-9\-]\{36\}\.jpg" ./test/81397018-b84a-11e0-9d2a-001b77dc0bed.jpg ./81397018-b84a-11e0-9d2a-001b77dc0bed.jpg $ find . -regextype foo -regex ".*/[a-f0-9\-]\{36\}\.jpg" find: Unknown regular expression type `foo'; valid types are `findutils-default', `awk', `egrep', `ed', `emacs', `gnu-awk', `grep', `posix-awk', `posix-basic', `posix-egrep', `posix-extended', `posix-minimal-basic', `sed'.
find sucht nach Dateien, Verzeichnissen und Unterverzeichnissen
Syntax
find [-H] [-L] [-P] [-D Fehlersuchoptionen] [-OStufe] [Startpunkt …] [Ausdruck]
Option
Option | Beschreibung |
---|---|
-name dateiname | Sucht Dateien des Namens dateiname |
-iname dateiname | Sucht nach dateiname und ignoriert Groß- und Kleinschreibung |
-size [+-]groesse[ck] | Sucht Dateien, die größer oder kleiner als groesse sind. c steht für Byte- und k für KByte-Werte. Ohne Einheitsangabe wird nach der Anzahl der belegten Blöcke (512 Byte) gesucht |
-type [fdl] | Sucht Dateien eines bestimmten Typs: f für einfache Dateien, d für Verzeichnisse und l für symbolische Links |
-atime [+-]zeit | Sucht Dateien, auf die seit mehr oder weniger als zeit Tagen nicht mehr zugegriffen wurde |
-amin [+-]zeit | Sucht Dateien, auf die seit mehr oder weniger als zeit Minuten nicht mehr zugegriffen wurde |
-mtime [+-]zeit | Sucht Dateien, die seit mehr oder weniger als zeit Tagen nicht mehr verändert wurden |
-mmin [+-]zeit | Sucht Dateien, die seit mehr oder weniger als zeit Minuten nicht mehr verändert wurden |
-perm 0000 | Sucht Dateien mit den den Zugriffsrechten 0000 (Oktalzahl) |
-user benutzername | Sucht Dateien des Benutzers benutzername |
-exec befehl „{}“ „;“ | Führt befehl mit den gefunden Dateien als Parameter aus. Die geschweiften Klammern stehen für den aktuell gefunden Dateinamen |
-ok befehl „{}“ „;“ | Führt befehl nach Rückfrage aus |