CLI-Adventskalender, Tag 8: find

CLI-Adventskalender: find

Avatar von Eric

Weil heute der 8. Dezember ist, geht es um find. Weil ihr fragt euch ja bestimmt auch, wo all die Zeit hingekommen ist und wieso das mit den Weihnachtsgeschenken schon langsam eng wird …

Man hat ja so selten Zeit zum aufräumen. Auch wenn ich – durch nachdrückliche Motivation seitens meiner Frau – inzwischen nicht mehr jedes Zimmer das ich bewohne in einen Drachenhort aus Müll verwandle. Auch wenn meine Schubladen inzwischen Label haben und die Grenzscharmützel in der Besteckschublade nur noch alle paar Monate aufkommen, sind meine digitalen Daten eine andere Geschichte.

Ich meine hier nicht die Git-Repos, an denen wir arbeiten. Die zwingen mich zur Ordnung (oder verstecken meinen Müll in den Ramschschubladen von

git stash
git stash). Ich meine den Ramsch außen herum. Ich spreche von meinen Download-Ordner, von den Schlachtfeldern alter Mayday-Projekte, von den externen Festplatten auf denen mein digitales Leben der letzten zwanzig Jahre liebevoll gesichert ist.

Wer soll da noch irgendwas finden? In endlicher Zeit? Mit granularen Filtern?

find

Glücklicherweise gibt es ja Suchfunktionen und das Terminal ist da keine Ausnahme.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
find /home/eric -name "passwords.kdbx"
find /home/eric -name "passwords.kdbx"
find /home/eric -name "passwords.kdbx"

find ist ein beeindruckendes Arbeitspferd. Es geht durch den kompletten Verzeichnisbaum und sucht alle vorkommenden Dateien, auf die die Filter zutreffen. Das heißt natürlich auf der anderen Seite, dass man es manchmal liebevoll von bestimmten Verzeichnissen wegsteuern möchte.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
find -name 'node_modules' -prune -o -name 'ThingExplainer.js' -print
find -name 'node_modules' -prune -o -name 'ThingExplainer.js' -print
find -name 'node_modules' -prune -o -name 'ThingExplainer.js' -print

Filter lassen sich also aneinanderreihen, ohne dass man auf Pipes zurückgreifen muss.

-prune
-prune bedeutet hier „wirf das hier weg“. Wenn das passiert ist
-prune
-prune true und der zweite Teil (hinter dem
-o
-o, Kurzform von
-or
-or) wird nicht mehr ausgewertet.

Natürlich lässt sich das auch gleich mit einer fuzzysearch verbinden und direkt in das entsprechende Programm füttern.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
cvlc "$(find ~/Downloads -name '*.mp3' | fzf)"
cvlc "$(find ~/Downloads -name '*.mp3' | fzf)"
cvlc "$(find ~/Downloads -name '*.mp3' | fzf)"
Goodies von Mayflower

Keine Sorge – Hilfe ist nah! Melde Dich unverbindlich bei uns und wir schauen uns gemeinsam an, ob und wie wir Dich unterstützen können.

Löschen, sammeln oder weiterverarbeiten?

Wer mal seinen inneren Dalek rauslassen will und alle Dateien eines bestimmten Typs systematisch loswerden möchte, freut sich über diesen Befehl:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
find git_repos/projekt -name "*.swp" -delete
find git_repos/projekt -name "*.swp" -delete
find git_repos/projekt -name "*.swp" -delete

find bringt außerdem ein eigenen Mechanismus mit, um Befehle auf jeder gefundenen Datei auszuführen.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
find /tagebuch -name "*.txt" -exec tar -rvf archiv.tar {} \;
find /tagebuch -name "*.txt" -exec tar -rvf archiv.tar {} \;
find /tagebuch -name "*.txt" -exec tar -rvf archiv.tar {} \;

Schlüssel ist hier das

-r
-r, dass alle Dateien in das bestehende Archiv hinzufügt.

Natürlich hält uns niemand davon ab, unseren alten Freund xargs zu bemühen.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
find /tmp -name '*mayflower*' -type f -print0 | xargs -I{} -0 cp {} /home/eric/will_ich_behalten/
find /tmp -name '*mayflower*' -type f -print0 | xargs -I{} -0 cp {} /home/eric/will_ich_behalten/
find /tmp -name '*mayflower*' -type f -print0 | xargs -I{} -0 cp {} /home/eric/will_ich_behalten/

-type f
-type f beschränkt uns hier auf Dateien. 
-print0
-print0 terminiert die Strings mit 0-Bytes anstelle von Zeilenumbrüchen. Das erlaubt uns Dateinamen mit Whitespace-Zeichen.

Granulare Filter

Okay, okay. Wir haben jetzt eine grobe Idee davon, was man damit machen kann. Welche Filter sind nun eigentlich möglich? So ziemlich alle Attribute, die eine Datei so hat. Alle Dateien größer als 50 Mb?

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
find /home/eric/Downloads -type f -size +50M
find /home/eric/Downloads -type f -size +50M
find /home/eric/Downloads -type f -size +50M

Alle Order die vor 2023 angelegt wurden? Ein wenig kniffliger, denn entweder nehmen wir

-mtime
-mtime und parsen ein Datum in Tagen rein …

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
find /home/eric/Documents -type d -mtime +$(($(date +%s -d '2023-01-01') / 86400 - $(date +%s) / 86400))
find /home/eric/Documents -type d -mtime +$(($(date +%s -d '2023-01-01') / 86400 - $(date +%s) / 86400))
find /home/eric/Documents -type d -mtime +$(($(date +%s -d '2023-01-01') / 86400 - $(date +%s) / 86400))


… oder wir negieren

-newermt
-newermt

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
find /home/eric/Documents -type d ! -newermt '2023-01-01'
find /home/eric/Documents -type d ! -newermt '2023-01-01'
find /home/eric/Documents -type d ! -newermt '2023-01-01'

Ein „älter als ein Jahr“ lässt

-mtime
-mtime wieder praktischer aussehen.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
find /home/eric/Documents -mtime +365
find /home/eric/Documents -mtime +365
find /home/eric/Documents -mtime +365

Wir können auch anhand von Besitzern suchen …

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
find /tmp -user kevin
find /tmp -user kevin
find /tmp -user kevin

… oder Gruppenzugehörigkeit.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
find /var/www -group www-data
find /var/www -group www-data
find /var/www -group www-data

Wir können auch nach gesetzten Berechtigungen suchen.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
find /etc -perm 0777
find /etc -perm 0777
find /etc -perm 0777

Und ein besonderes Highlight ist das Finden von leeren Verzeichnissen und Dateien.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
find /home/eric/repos -type d -empty
find /home/eric/repos -type d -empty
find /home/eric/repos -type d -empty

Engermaschige Netze

Alle diese Filter kann man beliebig kombinieren. Die logischen Operatoren verhalten sich so, wie man es erwartet.

  • -not
    -not oder ! bindet am stärksten.
  • -and
    -and oder
    -a
    -a bindet stärker als …
  • -or
    -or oder
    -o
    -o

Sobald ein Teil eines Oder-Blocks true ist, wird nichts weiter angewendet. Das ist wichtig, wenn man Blöcke mit

-prune
-prune („Wirf das hier weg, geh hier nicht tiefer, nichts zu sehen, weitergehen …“) und
-print
-print („Das möchte ich in meinem Output haben.“) kennzeichnet. Ein erfolgreiches
-prune
-prune evaluiert zu true.

Das

-and
-and ist in den meisten Fällen implizit, deswegen sieht man häufig nur
-o
-o.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
find /home/eric/repos/projekt (-name '.git' -o -name 'node_modules') -prune -o -type f -size +4M -newermt '2023-01-01' ! -name '*.jpg'
find /home/eric/repos/projekt (-name '.git' -o -name 'node_modules') -prune -o -type f -size +4M -newermt '2023-01-01' ! -name '*.jpg'
find /home/eric/repos/projekt (-name '.git' -o -name 'node_modules') -prune -o -type f -size +4M -newermt '2023-01-01' ! -name '*.jpg'

Alle Dateien im Projekt (außer in .git und node_modules), die größer als 4 Mb sind und seit Anfang diesen Jahres hinzugefügt wurden und keine jpg-Dateien sind.

Pipes rein und raus

Wir haben gesehen, dass man den Output von find leicht in Pipes wiederverwenden kann. Aber man kann auch eine Liste von Verzeichnissen als Startpunkte in find hineinpipen! Wichtig ist hier wieder, die Zeilenumbrüche in Nullbytes zu übersetzen.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
cat verzeichnisliste.txt | tr '\n' '\0' | find -files0-from=- -type f
cat verzeichnisliste.txt | tr '\n' '\0' | find -files0-from=- -type f
cat verzeichnisliste.txt | tr '\n' '\0' | find -files0-from=- -type f

find
find nistet sich also hervorragend in unseren Kommandozeilenwerkzeugkasten ein.

Shell-Weisheit des Tages
Bau Deine eigenen Werkzeuge!

Developerzeit ist kostbar. Dinge zu automatisieren ist verführerisch, dauert aber immer länger als man denkt. Dennoch …

Wenn man merkt, dass man dieselben Befehle immer wieder ausführt, wenn man merkt, dass sich Muster wiederholen, ist es vielleicht an der Zeit, ein kleines Skript zu schreiben.

Diese Weisheiten fangen langsam an, aufeinander aufzubauen. Da „Shell für die Ewigkeit“ ist, lohnt es sich also immer wieder, einen Blick in die manpages zu werfen. Aus diesem Grund – und weil „Automatisierung […] fest integriert“ ist – lohnt es sich, auf Kommandozeilenwerkzeuge zurückzugreifen. Vor allem iterativ.

Das Skript aus dem ersten Blogpost dieser Serie (mit dem man seine Stunden buchen kann), lässt sich mit Sicherheit noch mehr parametrisieren und erweitern. Es könnte zum Beispiel gleich auf das Ticket buchen, auf dessen Branch wir unterwegs sind.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
--- SCHNIPP ---
# feature/TICKET-123 -> TICKET-123
# genauso für pr/TICKET-123, bugfix/TICKET-123, usw.
ticket=$(git rev-parse --abbrev-ref HEAD 2>/dev/null | awk -F'/' '{print $NF}')
# awk splittet den String auf dem "/" und nimmt das letzte Feld. Mehr zu awk hinter einem anderen Adventstürchen :)
if [ -n "$2" ]
then
ticket=$2
fi
if [ -z "$ticket" ] || [ "$ticket" = "HEAD" ]
then
echo "Kein Git-Repo oder detached HEAD."
echo "Bitte Ticket-Nummer angeben."
exit 1
fi
--- SCHNIPP ---
--- SCHNIPP --- # feature/TICKET-123 -> TICKET-123 # genauso für pr/TICKET-123, bugfix/TICKET-123, usw. ticket=$(git rev-parse --abbrev-ref HEAD 2>/dev/null | awk -F'/' '{print $NF}') # awk splittet den String auf dem "/" und nimmt das letzte Feld. Mehr zu awk hinter einem anderen Adventstürchen :) if [ -n "$2" ] then ticket=$2 fi if [ -z "$ticket" ] || [ "$ticket" = "HEAD" ] then echo "Kein Git-Repo oder detached HEAD." echo "Bitte Ticket-Nummer angeben." exit 1 fi --- SCHNIPP ---
--- SCHNIPP ---
# feature/TICKET-123 -> TICKET-123
# genauso für pr/TICKET-123, bugfix/TICKET-123, usw.
ticket=$(git rev-parse --abbrev-ref HEAD 2>/dev/null | awk -F'/' '{print $NF}') 
# awk splittet den String auf dem "/" und nimmt das letzte Feld. Mehr zu awk hinter einem anderen Adventstürchen :)

if [ -n "$2" ]
then
  ticket=$2
fi

if [ -z "$ticket" ] || [ "$ticket" = "HEAD" ]
then
    echo "Kein Git-Repo oder detached HEAD."
    echo "Bitte Ticket-Nummer angeben."
    exit 1
fi
--- SCHNIPP ---

Wir wissen nie alles von vorne herein, selbst wenn es Arbeiten sind, die wir gefühlt hundertmal ausgeführt haben. Dieses „Bastler-Mindset“, über die eigene Arbeitsweise zu reflektieren, monotone Arbeiten nicht einfach hinzunehmen und immer wieder kleine Verbesserungen zu machen, ist das, was uns voranbringt. Das Leben ist geprägt von ständigem Lernen, Automatisierung ist nur die logische Fortführung desselbigen.

Goodies von Mayflower

Keine Sorge – Hilfe ist nah! Melde Dich unverbindlich bei uns und wir schauen uns gemeinsam an, ob und wie wir Dich unterstützen können.

Avatar von Eric

Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Für das Handling unseres Newsletters nutzen wir den Dienst HubSpot. Mehr Informationen, insbesondere auch zu Deinem Widerrufsrecht, kannst Du jederzeit unserer Datenschutzerklärung entnehmen.