CLI-Adventskalender, Tag 5: grep

CLI-Adventskalender: grep

Avatar von Eric

Am fünften Dezember beschäftigen wir uns mit einem der nützlichsten Tools: grep.

Je digitalisierter die Welt wird, desto mehr werden wir mit Informationen zugeschüttet. Tool nach Tool nach Tool schüttet uns mit Nachrichten und Fehlermeldungen und Debug-Informationen zu. Das Verhalten unseres eigenen Computers liegt in tausenden von Konfigurationsdateien, von unseren Servern ganz zu schweigen. Unsere Festplatten füllen sich nach und nach mit immer mehr semi-nützlichem Müll, wie die Garage eines Messies. Und je älter wir werden, desto schneller scheinen unsere Tage vorbeizugehen und desto weniger Zeit haben wir, um auch nur einen Teil dieser Situation zu meistern.

Wenn es doch nur ein Tool gäbe, das uns hilft, das Signal innerhalb des Rauschens zu finden. Ein Tool, das uns hilft, andere Werkzeuge dazu zu bringen zum Punkt zu kommen, sich kurz zu fassen, uns einfach nur das zu geben, was wir brauchen um weiterzukommen.

Dieses Tool heißt ChatGPT!

… nein, war nur Spaß. Es geht um grep. Steht ja im Titel.

grep

grep -ri "hostname" . – in welcher Datei hier im Ordner steht die Konfiguration, die ich suche? Durchsuche alle Dateien (-r) und ignoriere Groß- und Kleinschreibung (-i).

Diesen und die anderen zwei Standardfälle kennen die meisten.

grep -i "Error" ./application.log
sudo dmesg | grep -i "usb"

Aber dieses Tool kann noch so viel mehr. Schnallt euch an!

Ein paar bequeme Optionen

Manchmal will ich nur wissen wie oft etwas vorkommt.

grep -c "bottle" 99_bottles_of_beer.txt

Manchmal will ich die Zeilen davor und dahinter auch noch sehen.

grep -C2 "context" tutorial.json

Und manchmal reichen mir die ersten x Treffer.

grep -m4 "Error" *.log

Ein paar nützliche Optionen

Und manchmal möchte man einfach Sachen rausfiltern statt finden.

# Alle Zeilen, die nicht debug enthalten.
grep -v "debug" *.log

Vielleicht will ich auch einfach nur alle Dateien in diesen und in allen Unterverzeichnissen, in denen mein Name vorkommt (nur die Dateinamen, ich weiß ja wie ich heiße).

grep -lr "Eric" .

Aber, ähm … bitte nicht in .git suchen. Ich hab‘ ja nicht ewig Zeit.

grep -lr --exclude-dir=".git" "Eric" .

Ok, ganz nett, aber geht da eventuell noch mehr? Nun …

Ein paar mächtige Optionen

Reguläre Ausdrücke. Auch bekannt als der Grund, warum wir zwei Stunden gebraucht haben, um drei Zeilen Code zu schreiben.

Finde alle E-Mail-Adressen in diesen Dateien. Schaue auch in alle Unterverzeichnisse (-r), sag mir aber nicht in welchen Dateien sie waren (-h). Zeige mir nur die E-Mail-Adresse selbst (-o) und benutze die gruseligsten regulären Ausdrücke die Du kennst (-E).

grep -rhEo "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}" .

Der vorherige Absatz enthält zwei Lügen. Zum ersten, dieser reguläre Ausdruck kann nicht alle E-Mail-Adressen fehlerfrei erkennen.

Um überhaupt 99 Prozent aller E-Mail-Adressen nach RFC 5322 fehlerfrei zu erkennen, müsste der Ausdruck so aussehen (Copyright emailregex.com).

(?:[a-z0-9!#$%&'*+/=?^_{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])

Die zweite Lüge: -E schaltet nicht die gruseligsten regulären Ausdrücke frei. Mit grep -P kriegt man Perl-kompatible reguläre Ausdrücke!

grep -orPz "'\K[^']+(?='\s*\.tr\([^)]*\))" ./src | tr '\0' '\n' | cut -d':' -f2 | sort -u

Das hier ist ein echtes Beispiel aus meinem Arbeitsalltag. Wir haben Translation Strings in unserer App, in einem Format wie 'page-search-title'.tr(fallback: 'Search'). Dieser Befehl gibt mir eine Liste, die ich mit dem Inhalt der Datenbank vergleichen kann.

Eines der nützlichsten Unix-Werkzeuge

Ich benutze grep ständig. Viele diagnostische Werkzeuge liefern Unmengen an Output, Seiten über Seiten, von denen nur einzelne Zeilen relevant sind. Oft habe ich es mit mir unbekannten Verzeichnisstrukturen zu tun, in denen irgendwo die Datei mit den relevanten Einstellungen schlummert. Bevor ich anfange die Nadeln im Heuhaufen zu suchen, schließe ich doch lieber erstmal meinen Elektromagneten an.

Shell-Weisheit des Tages
Ökonomie der Informationen

Informationen und Statistiken sind überall … und die meisten von ihnen sind nutzlos. Eine der ersten Impulse die ich hatte als ich tiefer in die Shell eingestiegen bin, war es, so viele Informationen wie möglich anzuzeigen. Es sieht ja auch einfach cooler aus! Viele Screenshots von Konsolenfenstern zeigen den Output von top und neofetch und scrollenden Log-Outputs. Von ästhetischen Aspekten abgesehen sind diese Werkzeuge allerdings nur hin und wieder nützlich.

Als Entwickler verbringen wir viel Zeit damit, auf Code zu starren und die relevanten Informationen aus einem Ozean aus Text zu filtern. Oft bemerken wir gar nicht mehr, wie ermüdend die dadurch verursachte konstante mentale Auslastung ist. Also warum nicht etwas dagegen tun?

Der einfachste Fall ist es, Output mit einfachen Unix-Werkzeugen zu filtern.

for i in {1..254}; do
  ping -c 1 192.168.0.$i | grep "64 bytes" &> /dev/null
  if [ $? -eq 0 ]; then
    echo "192.168.0.$i is up"
  else
    echo "192.168.0.$i is down"
  fi
done

Oder APIs anstelle von Webbrowsern zu verwenden.

curl -u "$email:$api_token" \
-g -X GET \
  -H "Content-Type: application/json" \
  "https://jira.example.com/rest/api/2/search?jql=assignee=$username%20AND%20status%20in%20(Open,%20%22In%20Progress%22,%20Reopened)" \
  | jq '.issues[] | {key: .key, title: .fields.summary}'

Natürlich bietet sich hier auch curl an, wenn APIs nicht zur Verfügung stehen.

curl -s 'https://blog.mayflower.de' | pup 'h2.wp-block-post-title a json{}' | jq '.[].text'

Wenn eine Webseite JavaScript braucht, ist ein Browser immer noch die bequemste Option. Normalerweise würde ich hier mit Python arbeiten, aber zur Feier des Adventskalenders mache ich das heute mal in Bash:

chromium --headless --disable-gpu --dump-dom --virtual-time-budget=2000 https://blog.mayflower.de 2>/dev/null | pup 'h2.wp-block-post-title a json{}' | jq '.[].text'

(Nicht sehr robust, weil es einfach zwei Sekunden anstatt auf das Rendern spezifischer Elemente zu warten, aber für einen Einzeiler ohne Puppeteer durchaus brauchbar, finde ich.)

Es ist auch nichts verwerfliches daran, „Wegwerffenster“ zu öffnen. Ein schnelles Terminalfenster (immer lohnenswert, wenn man einen Hotkey damit belegt!), ein schneller Befehl, gefolgt von einem sofortigen exit, sobald man die nützlichen Infos herausgelesen hat, befreit den Bildschirmplatz und die eigene Gehirnkapazität.

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.