Xargs/Anwendung: Unterschied zwischen den Versionen

Aus Foxwiki
Keine Bearbeitungszusammenfassung
 
(10 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt)
Zeile 1: Zeile 1:
=== Dateinamen mit Leerzeichen ===
=== Dateinamen mit Leerzeichen ===
Üblicher Weise enthält der IFS das Leerzeichen, daher bricht ''xargs ''die Dateinamen dort auseinander.
; Ü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.
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(1)'' gibt man bekannt, er möge mit ASCII-NUL beendete Zeichenketten ausgeben, und xargs, er möge solche erwarten
Zeile 10: Zeile 10:


=== Eingabedatei als Parameter ===
=== Eingabedatei als Parameter ===
Sollen die Dateien verschoben werden, erwartet mv die Quelldateien als erstes, das Zielverzeichnis als letztes Argument.
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  
* Dieses Problem löst man bei ''xargs(1)'' genauso wie bei


<syntaxhighlight lang="bash" highlight="1" line>
<syntaxhighlight lang="bash" highlight="1" line>
Zeile 17: Zeile 17:
</syntaxhighlight>
</syntaxhighlight>


Die Zeichenkombination "{}" zeigt dem ''xargs(1)'', an welcher Stelle er die Argumentliste für das Kommando einzufügen hat.
Die Zeichenkombination "{}" zeigt dem ''xargs(1)'', an welcher Stelle er die Argumentliste für das Kommando einzufügen hat


=== Beispiele===
=== Beispiele===
Zeile 25: Zeile 25:
</syntaxhighlight>
</syntaxhighlight>


Finde Dateien mit dem Namen core in oder unterhalb des Verzeichnisses /tmp und lösche diese.
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.
* 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</nowiki>''' passen.
* Mit folgendem Befehl werden alle Dateien aus dem aktuellen Verzeichnis entfernt, die auf das Muster '''*.tmp</nowiki>''' passen
* Dabei werden Dateien, deren Namen Leerzeichen enthalten ebenfalls berücksichtigt: find . -name "*.tmp" -print0 | xargs -0 rm
* 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 {}.
* Als nächstes eine Anwendung mit der Ersatz-Zeichenkette {}
* Alle Dateien des Benutzers mit der uid 1001 werden nach '''/tmp/klaus/test''' verschoben.
* Alle Dateien des Benutzers mit der uid 1001 werden nach '''/tmp/klaus/test''' verschoben


; Achtung
; Achtung
* Die Verzeichnisstruktur wird dabei nicht wieder hergestellt.
* Die Verzeichnisstruktur wird dabei nicht wieder hergestellt
* Die Dateien landen wirklich alle in '''/tmp/klaus/test'''
* Die Dateien landen wirklich alle in '''/tmp/klaus/test'''


Zeile 42: Zeile 42:
* 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
* 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
* kann man mit xargs -a ~/installation/software.list sudo apt-get install
* die Pakete komfortabel alle auf einmal installieren.
* die Pakete komfortabel alle auf einmal installieren
* Dabei werden bereits installierte Pakete übersprungen.
* 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.
* 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


<syntaxhighlight lang="bash" highlight="1" line>
<syntaxhighlight lang="bash" highlight="1" line>
Zeile 50: Zeile 50:
</syntaxhighlight>
</syntaxhighlight>


* Dieser Befehl übergibt jeweils eine URL aus der Datei '''downloadlist''' an wget.
* Dieser Befehl übergibt jeweils eine URL aus der Datei '''downloadlist''' an wget
* Dabei wird wget vier mal jeweils mit einer anderen Datei gestartet.
* Dabei wird wget vier mal jeweils mit einer anderen Datei gestartet
* So werden vier Dateien auf einmal heruntergeladen und die Internetverbindung optimal ausgenutzt.
* So werden vier Dateien auf einmal heruntergeladen und die Internetverbindung optimal ausgenutzt


=== Weitere Möglichkeiten ===
=== Weitere Möglichkeiten ===
This command is equivalent to using ''find'' with ''xargs'', only a bit shorter and more efficient.
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).
* 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''... ] ]
* 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.
* 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.
Here's an example of efficiently copying found files to ''/tmp'', in a POSIX-compliant way
<syntaxhighlight lang="bash" highlight="1" line>
<syntaxhighlight lang="bash" highlight="1" line>
  find . -name '*.txt' -type f \ -exec sh -c 'exec cp -f "$@" /tmp' X '{}' +
  find . -name '*.txt' -type f \ -exec sh -c 'exec cp -f "$@" /tmp' X '{}' +
</syntaxhighlight>
</syntaxhighlight>
Obvious, simple, and readable, isn't it? Perhaps not, but worth knowing since it is safe, portable, and efficient.
Obvious, simple, and readable, isn't it? Perhaps not, but worth knowing since it is safe, portable, and efficient


=== Beispiel 1 ===
=== Beispiel 1 ===
Wo in meinem Homeordner liegt die Datei "test.pdf"? find ~ -name test.pdf
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.
* 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...
* Irgendwo in eurem Homeordner, irgendein Name, vor ein paar Minuten


<syntaxhighlight lang="bash" highlight="1" line>
<syntaxhighlight lang="bash" highlight="1" line>
Zeile 78: Zeile 78:
* ~ -- rekursiv in meinem Homeordner
* ~ -- rekursiv in meinem Homeordner
* -type f -- eine Datei (kein Ordner...)
* -type f -- eine Datei (kein Ordner...)
* ! -path '*/.*' -- ohne Dateien oder Ordner, die mit Punkt beginnen.
* ! -path '*/.*' -- ohne Dateien oder Ordner, die mit Punkt beginnen
* -mmin -10 -- nicht älter als 10 Minuten
* -mmin -10 -- nicht älter als 10 Minuten
* -ls -- lange Anzeige (wie ls -l)
* -ls -- lange Anzeige (wie ls -l)
* Variante: mit Dotfiles, nicht aber Ordner, die mit Punkt beginnen (also ohne .gnome/ oder .kde/ und so weiter, aber mit .bash_history).
* Variante: mit Dotfiles, nicht aber Ordner, die mit Punkt beginnen (also ohne .gnome/ oder .kde/ und so weiter, aber mit .bash_history)
* Und zwar alle Dateien, die nicht älter als zwei Tage sind.
 
find ~ ! -path '*/.*/*' -type f -ctime -2
Und zwar alle Dateien, die nicht älter als zwei Tage sind
* Im Homeordner alle Dateien finden, deren Namen Leerzeichen enthalten.
 
* Wieder ohne Dateien oder Ordner, die mit Punkt beginnen.
<syntaxhighlight lang="bash" highlight="1" line>
find ~ ! -path '*/.*/*' -type f -ctime -2
</syntaxhighlight>
 
Im Homeordner alle Dateien finden, deren Namen Leerzeichen enthalten
 
Wieder ohne Dateien oder Ordner, die mit Punkt beginnen
<syntaxhighlight lang="bash" highlight="1" line>
  find $HOME ! -path '*/.*' -type f -name '* *'
  find $HOME ! -path '*/.*' -type f -name '* *'
* $HOME -- ist gleichbedeutend mit ~ * -name '* *' -- Name enthält mindestens ein Leerzeichen
</syntaxhighlight>
 
$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)
* 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.
* Im Ordner public_html alle HTML und PHP Dateien ausfindig machen, in denen ".mp3" erwähnt wird
* Nur die Dateinamen ausgeben.
* Nur die Dateinamen ausgeben


<syntaxhighlight lang="bash" highlight="1" line>
<syntaxhighlight lang="bash" highlight="1" line>
Zeile 96: Zeile 105:
</syntaxhighlight>
</syntaxhighlight>


Zu beachten: ( ) * müssen vor der Bash versteckt, also mit Backslash escaped werden.
Zu beachten: ( ) * müssen vor der Bash versteckt, also mit Backslash escaped werden
* Andere Methode (-exec statt xargs), gleiches Ergebnis.
* Andere Methode (-exec statt xargs), gleiches Ergebnis


<syntaxhighlight lang="bash" highlight="1" line>
<syntaxhighlight lang="bash" highlight="1" line>
Zeile 103: Zeile 112:
</syntaxhighlight>
</syntaxhighlight>


Zu beachten: -exec mit''' \+''' abschließen statt mit '''\; '''beschleunigt das Abarbeiten ungemein, weil dann mehrere grep Prozesse parallel gestartet werden (vgl. xargs).
Zu beachten: -exec mit''' \+''' abschließen statt mit '''\; '''beschleunigt das Abarbeiten ungemein, weil dann mehrere grep Prozesse parallel gestartet werden (vgl. xargs)
* Setzt eine halbwegs aktuelle Version von find voraus.
* Setzt eine halbwegs aktuelle Version von find voraus
* Hoppla, da sind auch Dateien mit Leerzeichen im Namen dabei? Kein Problem.
* 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'
 
<syntaxhighlight lang="bash" highlight="1" line>
find ~/public_html \( -name \*.html -o -name \*.php \) -print0 | xargs -0 grep -l '.mp3'
</syntaxhighlight>
 
* finds Option -print0 erzeugt die richtige Ausgabe für xargs Option -0
* finds Option -print0 erzeugt die richtige Ausgabe für xargs Option -0
* Anderer Einsatzzweck: Plattenplatz wird knapp, wo sind die großen Dateien?
* Anderer Einsatzzweck: Plattenplatz wird knapp, wo sind die großen Dateien?
* Also zum Beispiel: in meinem Homeordner alle Dateien, die größer als 500MB sind.
* Also zum Beispiel: in meinem Homeordner alle Dateien, die größer als 500MB sind


<syntaxhighlight lang="bash" highlight="1" line>
<syntaxhighlight lang="bash" highlight="1" line>
Zeile 118: Zeile 131:
* Alle Dateien/Ordner in meinem Homeordner, die root gehören: find ~ -user root
* 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,
* 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".
* also Lese/Schreib/Ausführrechte für alle haben, lange Ausgabe wie "ls -l"


<syntaxhighlight lang="bash" highlight="1" line>
<syntaxhighlight lang="bash" highlight="1" line>
Zeile 124: Zeile 137:
</syntaxhighlight>
</syntaxhighlight>


Welche Art von Dateien (Mimetype) liegen im Ordner Documents, mit Rücksicht auf Dateien mit Leerzeichen im Namen, nur in diesem Ordner, keine Unterordner:
Welche Art von Dateien (Mimetype) liegen im Ordner Documents, mit Rücksicht auf Dateien mit Leerzeichen im Namen, nur in diesem Ordner, keine Unterordner
 
<syntaxhighlight lang="bash" highlight="1" line>
<syntaxhighlight lang="bash" highlight="1" line>
find ~/Documents/ -maxdepth 1 -type f -print0 | xargs -0 file
find ~/Documents/ -maxdepth 1 -type f -print0 | xargs -0 file
</syntaxhighlight>
</syntaxhighlight>


Find sucht immer rekursiv, es sei denn, man schränkt mit "-maxdepth" die Tiefe ein.
Find sucht immer rekursiv, es sei denn, man schränkt mit "-maxdepth" die Tiefe ein
* Die Liste ließe sich beliebig fortsetzen.
* Die Liste ließe sich beliebig fortsetzen
* Wie vielseitig find ist, zeigt sich schon an der Länge der manpage.
* Wie vielseitig find ist, zeigt sich schon an der Länge der manpage
* Find findet nach Name, Regular expression, Größe, Datum, Dateityp,...
* Find findet nach Name, Regular expression, Größe, Datum, Dateityp,
* und so weiter und läßt sich mit "-exec" und der Pipe für "xargs" zu beinahe allem verwenden, was nur auf bestimmte Dateien/Ordner angewandt werden soll.
* und so weiter und läßt sich mit "-exec" und der Pipe für "xargs" zu beinahe allem verwenden, was nur auf bestimmte Dateien/Ordner angewandt werden soll
* Darum wird das Kommando in vielen Shellskripten verwendet.
* Darum wird das Kommando in vielen Shellskripten verwendet


=== Beispiel 2 ===
=== Beispiel 2 ===
Zeile 143: Zeile 155:
</syntaxhighlight>
</syntaxhighlight>


Dateien finden, deren Namen nicht einem bestimmten Muster entsprechen:
Dateien finden, deren Namen nicht einem bestimmten Muster entsprechen


<syntaxhighlight lang="bash" highlight="1" line>
<syntaxhighlight lang="bash" highlight="1" line>
Zeile 149: Zeile 161:
</syntaxhighlight>
</syntaxhighlight>


Dateien finden, deren Namen einem bestimmten Muster entsprechen und die einen bestimmten Text enthalten:
Dateien finden, deren Namen einem bestimmten Muster entsprechen und die einen bestimmten Text enthalten
<syntaxhighlight lang="bash" highlight="1" line>
<syntaxhighlight lang="bash" highlight="1" line>
find . -name "*.php" -exec grep -il "suchtext" {} \;
find . -name "*.php" -exec grep -il "suchtext" {} \;
</syntaxhighlight>
</syntaxhighlight>


Dateien finden, die bestimmte Datei-Endungen haben (Mit Regular-Expressions finden):
Dateien finden, die bestimmte Datei-Endungen haben (Mit Regular-Expressions finden)
<syntaxhighlight lang="bash" highlight="1" line>
<syntaxhighlight lang="bash" highlight="1" line>
find . -regex ".*(php|html|tpl)$"
find . -regex ".*(php|html|tpl)$"
Zeile 164: Zeile 176:
</syntaxhighlight>
</syntaxhighlight>


Dateien finden, deren Pfade einem bestimmten Pattern entsprechen:
Dateien finden, deren Pfade einem bestimmten Pattern entsprechen
<syntaxhighlight lang="bash" highlight="1" line>
<syntaxhighlight lang="bash" highlight="1" line>
find . -path "*/.svn*"
find . -path "*/.svn*"
</syntaxhighlight>
</syntaxhighlight>


Dateien finden, deren Pfade nicht einem bestimmten Pattern entsprechen:
Dateien finden, deren Pfade nicht einem bestimmten Pattern entsprechen
<syntaxhighlight lang="bash" highlight="1" line>
<syntaxhighlight lang="bash" highlight="1" line>
find . \! -path "*/.svn*"
find . \! -path "*/.svn*"
</syntaxhighlight>
</syntaxhighlight>


Dateien finden, die nicht einem bestimmten User (nicht root) gehören:
Dateien finden, die nicht einem bestimmten User (nicht root) gehören
<syntaxhighlight lang="bash" highlight="1" line>
<syntaxhighlight lang="bash" highlight="1" line>
find . \! -user root
find . \! -user root
</syntaxhighlight>
</syntaxhighlight>


Dateien finden, die “oo” oder “ee” im Namen haben:
Dateien finden, die “oo” oder “ee” im Namen haben
<syntaxhighlight lang="bash" highlight="1" line>
<syntaxhighlight lang="bash" highlight="1" line>
find . \( -name "*oo*" -or -name "*ee*" \)
find . \( -name "*oo*" -or -name "*ee*" \)
</syntaxhighlight>
</syntaxhighlight>


Dateien finden, die nicht “oo” oder “ee” im Namen haben:
Dateien finden, die nicht “oo” oder “ee” im Namen haben
<syntaxhighlight lang="bash" highlight="1" line>
<syntaxhighlight lang="bash" highlight="1" line>
find . \! \( -name "*oo*" -or -name "*ee*" \)
find . \! \( -name "*oo*" -or -name "*ee*" \)
Zeile 193: Zeile 205:
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)$”
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)$”
</syntaxhighlight>
</syntaxhighlight>
 
es wird nix ausgegeben :(
Keine Ausgabe


=== Beispiel 3 ===
=== Beispiel 3 ===
Angenommen, Sie möchten eine Liste der Verzeichnisse in ''/usr/share'' erhalten, dann tippen Sie: find /usr/share -type d
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.
* 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.
* 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''.
* 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.
* Sie möchten diese Dateien in das Verzeichnis ''/home/httpd/obsolete'' verknüpfen
* Geben Sie folgendes ein:[http://www.murnauer.de/mandrakeref/command-find.html#FTN.AEN4397 []
* Geben Sie folgendes ein:[http://www.murnauer.de/mandrakeref/command-find.html#FTN.AEN4397 []


Zeile 210: Zeile 222:
</syntaxhighlight>
</syntaxhighlight>


* Gut, das hier ist etwas komplex und verlangt nach Erklärung.
* Gut, das hier ist etwas komplex und verlangt nach Erklärung
* Das Suchkriterium ist Folgendes: \( -name "*.htm" -o -name "*.html" \) -a -ctime -30
* 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'').
* 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.
* 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.
* 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.
* 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.
* 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.
* 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 \;
* 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 '';</nowiki>'' vor der Shell schützen, da diese es sonst als Kommandoseparator interpretiert und find sich beschweren wird, dass ''-exec'' ein Argument fehlt.
* Auch hier müssen Sie das '';</nowiki>'' 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
; Ein letztes Beispiel
* Sie haben ein großes Verzeichnis mit allen möglichen Bilddateien: ''/shared/images''.
* 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.
* 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.
* 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''.
* 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'':
Sie möchten nicht im Verzeichnis ''old'' suchen, Sie möchten diese Liste zugeschickt bekommen und Ihr Benutzername ist ''john''
<syntaxhighlight lang="bash" highlight="1" line>
<syntaxhighlight lang="bash" highlight="1" line>
find /shared/images -cnewer \ /shared/images/stamp \ -a -iregex ".*\.jpe?g" \ -a -not -regex ".*/old/.*" \ | mail john -s "Neue Images"
find /shared/images -cnewer \ /shared/images/stamp \ -a -iregex ".*\.jpe?g" \ -a -not -regex ".*/old/.*" \ | mail john -s "Neue Images"
Zeile 234: Zeile 246:
Nun wäre es nicht sehr schön, dieses Kommando regelmäßig neu eingeben zu müssen, also brauchen Sie…?
Nun wäre es nicht sehr schön, dieses Kommando regelmäßig neu eingeben zu müssen, also brauchen Sie…?


=== Problembehebung ===
== Sieh auch ==
{{Special:PrefixIndex/xargs}}
----
{{Special:PrefixIndex/find}}
----
{{Special:PrefixIndex/grep}}

Aktuelle Version vom 23. Dezember 2024, 14:59 Uhr

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 / -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

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</nowiki> 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

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/ und so weiter, 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 parallel 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 zum Beispiel: 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, Regular expression, Größe, Datum, Dateityp,
  • und so weiter und läßt sich mit "-exec" und der Pipe für "xargs" zu beinahe allem verwenden, was nur auf bestimmte Dateien/Ordner angewandt 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)$”

Keine Ausgabe

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 ;</nowiki> 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…?

Sieh auch