Xargs macht Dampf

Aus LinuxUser 11/2025

Xargs macht Dampf

© Akhararat Wathanasing / 123RF.com

Turbolader

Unter den klassischen Kommandozeilen-Utilities ist Xargs so etwas wie das Phantom. Nach der Lektüre dieses Artikels könnte es Ihr Begleiter sein.

Wer kennt nicht die klassische Problemstellung: Wie finde ich Text in großen Mengen von Dateien? Früher oder später erwischt es jeden. Dann wird es oft hektisch – völlig zu Unrecht. Ich habe in den Ausgaben 11/2023 [1] und 12/2023 [2] schon einmal der Macht von Find gehuldigt, dabei aber dessen wohl fähigsten Begleiter sträflich vernachlässigt. Das muss sich ändern.

Der eingangs formulierten Aufgabenstellung könnte ich mich halbwegs naiv nähern, indem ich schlicht und einfach Grep mit entsprechenden Parametern (Listing 1, erste Zeile) auf das Problem loslasse. Diese Herangehensweise endet gern mit dem Fehler aus der zweiten Zeile von Listing 1. Da haben wir nun den Salat: Im Zielordner gibt es zu viele Dateien. Zu viele, um sie Grep als Parameter durch die Bash übergeben zu lassen.

Listing 1

Grep

$ grep -Hni needle haystack/*
bash: /usr/bin/grep: Argument list too long
$ find . -type f -exec grep -H -n -i needle {} \;

Deswegen muss ich anders ansetzen und greife auf Find zurück. Wer sich kurz mit dem Tool beschäftigt, stolpert schnell über den Parameter -exec. Mit diesem Ansatz übergibt Find dem Kommando nach -exec die aktuelle Fundstelle, gekennzeichnet durch ein geschweiftes Klammerpaar (Listing 1, letzte Zeile). Ein abschließendes, per Backslash entschärftes Semikolon sorgt dafür, dass Find das Ende des Arguments von -exec erkennt.

In der Tat löst dieser Ansatz mein Problem, allerdings zu einem Preis. Was hier passiert, ist langsam und vor allem verschwenderisch. Für jede Fundstelle muss Find einen eigenen Grep-Prozess starten, egal, ob es den Suchbegriff, den Grep finden soll, überhaupt in der jeweiligen Datei gibt. Das geht viel effizienter, und zwar mit Xargs.

Traumpaar

Viel besser als die Fundstellen einzeln per Grep zu bearbeiten, ist es, sie über eine Pipe an Xargs zu delegieren. Das unterscheidet sich gar nicht so sehr von der Variante via -exec. Das erste Kommando in Listing 2 sucht nach Dateien, die auf .txt enden. Doch dazu wird nicht für jede einzelne Grep gestartet; stattdessen übernimmt xargs die Übergabe der Fundstellen an grep.

Das ist erheblich effizienter, denn Xargs leitet nicht nur eine Datei als Argument weiter, sondern mehrere. Wie viele es genau sind, hängt stark vom Betriebssystem ab, beispielsweise von den Security Limits. Klar: Wer einem Programm unbegrenzt Parameter übergeben darf, schafft es vielleicht, darüber eine Attacke gegen das Betriebssystem zu fahren. Letztlich bestimmen ein paar Variablen den Spielraum von Xargs – dazu später mehr.

Ich habe also Find und Grep mit Xargs dazwischen verkittet. Das läuft wie geschmiert und deutlich flotter als zuvor. Dummerweise erhalte ich aber jetzt zahlreiche Fehler. Es scheint, als gäbe es Probleme bei Ordnern und Dateien mit Leerzeichen im Namen. Eine kurze Überprüfung zeigt, dass Xargs in diesem Fall Wort für Wort an Grep schickt, was mangels existierender Dateien nicht funktionieren kann. Bei Grep kommt möglicherweise der Code aus der zweiten Zeile von Listing 2 an.

Dass Linux, die Bash und sämtliche Userspace-Werkzeuge mit Leerzeichen im Dateinamen zurechtkommen, steht außer Frage. Ungünstig ist hier lediglich, dass Find keinen Unterschied zwischen Leerzeichen im Dateinamen und einem Leerzeichen als Trennzeichen zwischen Dateien macht. Genau da liegt das Problem. Glücklicherweise verfügen sowohl Find als auch Xargs über eine Lösung dafür.

Ich kann Find dazu veranlassen, die Funde per binärer 0 terminiert an Xargs zu übergeben. Das klappt mithilfe des Schalters print0. Xargs wiederum bietet mit dem Schalter -0 exakt das passende Gegenstück dazu. Beide Tools ergänzen sich hier ausgezeichnet. Somit wird aus dem Kommando in der ersten Zeile das aus der dritten Zeile von Listing 2.

Listing 2

Grep und Xargs

$ find . -type f -name "*.txt" | xargs grep -Hni needle
$ grep -Hni needle haystack/2025-10 Artikel Linux Magazin.txt/
$ find . -type f -print0 -name "*.txt" | xargs -0 grep -Hni needle

Optimal durchdacht

Selbstverständlich bleibt es nicht bei einem trivialen Aufruf von Xargs. Das Tool wartet mit weitaus mehr Möglichkeiten auf. Mit --max-args (kurz -n) schnürt es handliche Pakete, die ich wiederum an Grep weiterreichen kann. Das hat den Vorteil, dass ich die Suche nicht, wie oben erwähnt, über nur eine Datei abfeuere, sondern gleich über beispielsweise 50 Dateien. Das reduziert Overhead.

Dabei sollten Sie jedoch im Hinterkopf behalten, dass sich bestimmte Parameter wie --max-args und --max-chars (kurz -s) gegenseitig beeinflussen. Logischerweise kann --max-args nicht größer als --max-chars sein. Immerhin enthält --max-chars zu den Parametern und deren Argumenten auch noch die Trennzeichen. Zur Beruhigung kann ich allerdings anführen, dass es mir noch nie unbeabsichtigt gelungen ist, Xargs zu überfordern.

Mit dem Parameter --max-procs bringt Xargs ein mächtiges Feature mit. Der Parameter – mein persönlicher Liebling – ermöglicht, die Anzahl der parallel auszuführenden Prozesse zu steuern. Das heißt, mit --max-procs 4 grep ... (kurz -P) könnte ich die Find-Suchergebnisliste mit vier Grep-Instanzen durchsuchen. Auf modernen CPUs erscheint das dem geneigten User wie der Heilige Gral. Tatsächlich gibt es beim parallelen Greppen noch andere Parameter. Grep gewinnt durch das Parallelisieren nicht notwendigerweise Schnelligkeit, vor allem nicht auf rotierenden Platten – schließlich müssten die Schreib/Leseköpfe sehr häufig bewegt werden. Selbst auf einer NVMe-SSD ist also der erste Befehl aus Listing 3 schneller als der zweite.

Listing 3

Schnelligkeit

$ find . -type f -print0 -name "*.txt" | \
  xargs -0 grep -Hni NEEDLE
$ find . -type f -print0 -name "*.txt" | \
  xargs -0 -P 4 -n 40 grep -Hni NEEDLE

Ganz anders sieht die Situation bei der Parallelisierung aufwendiger Tasks aus, etwa beim Encoding oder Transcoding. Deutlich spürbar wird das, wenn ich nicht greppe, sondern zum Beispiel eine Vielzahl von WAV-Dateien zu Ogg Vorbis encodiere. In diesem Fall lassen sich die einzelnen Tasks gut auf eigene Prozesse verteilen. Hier besteht kein Synchronisierungsbedarf, da es nicht vorkommt, dass mehrere Prozesse gleichzeitig schreibend auf ein gemeinsames Objekt zugreifen – beinahe. Tatsächlich teilen sich die Prozesse die Standardausgabe, was durchaus verwirren kann.

Auf einer Multi-Core-CPU kann je ein Kern einen eigenen Oggenc-Prozess bedienen. Die Anzahl der CPU-Kerne lässt sich einfach über das Standard-Tool Nproc einstellen. Ich habe mir für diesen Benchmark zwei Fälle vorgenommen, wie in Listing 4 zu sehen. Im ersten Fall lasse ich testhalber eine Reihe von WAV-Dateien sequenziell direkt von Oggenc umwandeln. Im zweiten Fall übergebe ich die Dateien per Find an Xargs. Dabei soll Xargs die von Find übergebenen Werte, ersetzt durch %, an Oggenc weiterreichen. In beiden Fällen wird mittels Time die Zeit gestoppt.

Listing 4

Oggenc vs. Oggenc parallel

$ time oggenc -q9 *.wav
$ time find . -type f -iname "*.wav" -print0 | xargs -0 -n 1 -I % -P $(nproc) /usr/bin/oggenc -q9 %

Bevor Sie sich auf die Ergebnisse stürzen, beachten Sie bitte die Xargs-Parameter -n 1 und -I %. Mit dem ersten erzwinge ich, dass an Oggenc genau ein Dateiname zu genau einer WAV-Datei übergeben wird. Das ist unbedingt notwendig, da Oggenc sonst mit der sequenziellen Encodierung beginnen würde. Der zweite Parameter -I % erweist sich als noch interessanter. Er bewirkt, dass die an Xargs übergebenen Dateinamen im Platzhalter % gespeichert werden, den ich ganz am Schluss der Parameterkette verwende. Der Schalter -q9 veranlasst hier lediglich, dass Oggenc bei der Enkodierung nahezu das Maximum an Qualität ausschöpft.

Wenig überraschend fällt der Unterschied beachtlich aus. Time misst die Laufzeiten der Kommandos und zeigt bei der Echtzeit eine deutliche Abweichung. Dank paralleler Verarbeitung beendet die Xargs-Variante dieselbe Aufgabe in rund einem Drittel der Zeit wie die sequenzielle Variante.

Selbst nach zahlreichen Jahren mit Linux war mir nie vollkommen klar, was genau die drei Zeiten bedeuten, die Time (Listing 5) liefert. Dank Xargs bin ich nun ein wenig schlauer, denn die Manpage von Time liefert die gesuchten Informationen. Mit real ist die Wall-Clock-Zeit gemeint, also die tatsächlich vergangene Zeit vom Start bis zur Beendigung des Prozesses. Beim Begriff tatsächliche oder reale Zeit rotiert Albert Einstein vermutlich im Grab, darum ist “Zeit nach Newton” womöglich die bessere Formulierung.

Listing 5

Laufzeiten

# sequenzielle Verarbeitung
        real    0m53,998s
        user    0m52,653s
        sys     0m1,265s
# parallele Verarbeitung:
        real    0m16,871s
        user    1m5,848s
        sys     0m1,581s

Die user-Zeit wiederum ist die Zeit, die die CPU im User-Mode operiert, also Nicht-Kernel-Code innerhalb des Prozesses ausführt. Somit handelt es sich bei der sys-Zeit um die Zeit, die die CPU im Prozess in Kernel-Code verbringt. Seien Sie gewarnt: Die user– addiert mit der sys-Zeit muss nicht notwendigerweise die real-Zeit ergeben: Es fehlen die Zeiten für den Prozesswechsel sowie Wartezeiten auf andere Betriebsmittel wie unter anderem Disk I/O.

Außer den besagten Parametern möchte ich noch den Schalter -t erwähnen. Entgegen aller gewohnten Konventionen aktiviert er den Verbose-Mode, zum Beispiel zu Debugging-Zwecken. Außerdem bietet Xargs mit --show-limits die Möglichkeit, die tatsächlichen Größen für Parameter auf dem aktuellen System zu erfragen. Dementsprechend finden Sie darüber die maximale Gesamtlänge der Argumente sowie die maximale Befehlslänge heraus.

Cleveres Sammeln

Es kommt immer wieder vor, dass ich Dateien mit ganz bestimmten Merkmalen in einem separaten Ordner zusammenfassen möchte – Protokolle beispielsweise, um sie leichter nebeneinander korrelieren zu können. Dazu hat Xargs mit Platzhaltern das richtige Werkzeug parat. Im Oggenc-Beispiel habe ich bereits auf Platzhalter hingewiesen, hier kommen sie erneut zum Einsatz.

Angenommen, ich möchte die Log-Dateien von Datenbank, Webserver, Betriebssystem und so weiter der letzten drei Tage im Ordner LogAnalyse/ haben. Mit Find wähle ich die entsprechenden Protokolldateien aus, Xargs darf sich anschließend um die Kopiervorgänge kümmern. Genau das erledigt der erste Aufruf aus Listing 6.

Listing 6

Platzhalter

$ find /var/log/ -mtime -3 -print0 | \
  xargs -0 -I {} cp -av {} ~/temp/LogAnalyse
$ find . -type f -name "*.txt" | \
  xargs -I {} sh -c 'ls -l {}; du -h {}'

Find schreibt wieder per -print0 Null-terminierte Dateinamen nach Stdout. Xargs nimmt sie per -0 entgegen und bewahrt sie im Platzhalter {} auf, um sie so dem Kopierwerkzeug Cp als ersten Parameter zu übergeben. Bitte beachten Sie, dass bei Cp Quelle und Ziel vertauscht wären, falls Sie diesen Platzhalter weglassen würden. Ohne den Platzhalter würde Xargs schlicht die jeweils aktuelle Log-Datei als letzten Parameter an Cp übergeben, sie also als Kopierziel und nicht als Kopierquelle verwenden.

Selbstverständlich kann Xargs nicht nur an ein zusätzliches Kommando weiterleiten. Es gibt Möglichkeiten, eine per Platzhalter gespeicherte Zeichenkette auch an mehrere Programme zu übergeben. Statt direkt einen Prozess zu starten, stößt der zweite Aufruf aus Listing 6 eine weitere Shell an, die dann durch Xargs die Platzhalter erhält. So lässt sich in einem Befehl nicht nur das Verzeichnis per Ls auflisten, sondern direkt hinterher mit Du auch der Speicherbedarf anzeigen.

Fazit

Xargs gehört zu den heimlichen Helden unter Linux. Es ist definitiv eines der Tools, bei denen ich mich stets gefragt habe, warum ich mich nicht schon viel früher damit beschäftigt habe. Gerade im Zusammenspiel mit Find spielt Xargs alle Trümpfe aus. Das gilt vor allem, wenn es um aufwendige Anpassungen an vorhandenen Dateien geht, zum Beispiel Kodierungsvorgänge, Anpassungen an EXIF-Daten und vieles mehr. (csi/jlu)

Infos

  1. Find ausreizen: Thomas Reuß, “Treffsicher”, LU 11/2023, S. 16, https://www.linux-community.de/49785

  2. Regex (Teil 1): Thomas Reuß, “Schreckgespenster”, LU 12/2023, S. 34, https://www.linux-community.de/49924

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDF
LinuxUser 11/2025 KAUFEN
EINZELNE AUSGABE
ABONNEMENTS
TABLET & SMARTPHONE APPS
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben