grep: "Die Argumentliste ist zu lang"
Moin moin, wenn ich mit grep in Verzeichnissen mit sehr vielen Dateien, z.B. meinem News-Spool, suchen lassen will, bekomme ich immer die Fehlermeldung "Die Argumentliste ist zu lang". Wißt Ihr dafür 'n "Gegenmittel"? Danke schon mal, und tschö Jürgen -- Wer das erste Knopfloch verfehlt, kommt mit dem Zuknöpfen nicht zurande. Goethe
Am 12.11.2002 um 17:33 schrieb Jürgen Hein:
wenn ich mit grep in Verzeichnissen mit sehr vielen Dateien, z.B. meinem News-Spool, suchen lassen will, bekomme ich immer die Fehlermeldung "Die Argumentliste ist zu lang".
Der Wildcard, den du wahrscheinlich verwendest wird vor der Ausführung des Befehls expandiert. D.h. durch eine mit Leer- zeichen getrennte Liste der Dateinamen ersetzt. Für diese Argumentliste gibt es ein festes Limit: dennis@galileo:~> getconf ARG_MAX 131072
Wißt Ihr dafür 'n "Gegenmittel"?
Ja, z.B. mit find und/oder xargs. MfG, Dennis -- Dennis Stosberg eMail: dennis@stosberg.net gpg key: http://stosberg.net/dennis.asc icq: 63537718
On Die, 12 Nov 2002 at 17:48 (+0100), Dennis Stosberg wrote:
Am 12.11.2002 um 17:33 schrieb Jürgen Hein:
wenn ich mit grep in Verzeichnissen mit sehr vielen Dateien, z.B. meinem News-Spool, suchen lassen will, bekomme ich immer die Fehlermeldung "Die Argumentliste ist zu lang".
Der Wildcard, den du wahrscheinlich verwendest wird vor der Ausführung des Befehls expandiert. D.h. durch eine mit Leer- zeichen getrennte Liste der Dateinamen ersetzt. Für diese Argumentliste gibt es ein festes Limit:
dennis@galileo:~> getconf ARG_MAX 131072
Wißt Ihr dafür 'n "Gegenmittel"?
Ja, z.B. mit find und/oder xargs.
Oder eine Schleife: for i in *; do tue_dies_und_jenes_mit $i done Jan
'n Abend! Am 12.11.2002 um 21:11 schrieb Jan Trippler:
Ja, z.B. mit find und/oder xargs.
Oder eine Schleife: for i in *; do tue_dies_und_jenes_mit $i done
Und warum sollte der Wildcard hier nicht expandiert werden? MfG, Dennis -- Dennis Stosberg eMail: dennis@stosberg.net gpg key: http://stosberg.net/dennis.asc icq: 63537718
On Die, 12 Nov 2002 at 22:38 (+0100), Dennis Stosberg wrote:
Am 12.11.2002 um 21:11 schrieb Jan Trippler:
Ja, z.B. mit find und/oder xargs.
Oder eine Schleife: for i in *; do tue_dies_und_jenes_mit $i done
Und warum sollte der Wildcard hier nicht expandiert werden?
Sie wird expandiert, aber es ist nicht die Kommandozeile - die Begrenzung schlägt hier nicht zu. Die 128k Zeichen limitieren die Länge der Komandozeilenargumente. Probiers aus: jan@k500:~/tmp/args> for i in `seq 1 10000`; do
touch abcdefghijklmnopqrstuvwxyz$i done jan@k500:~/tmp/args> ls * bash: /bin/ls: Die Argumentliste ist zu lang jan@k500:~/tmp/args> for i in *; do rm $i; done jan@k500:~/tmp/args>
Jan
Am 12.11.2002 um 23:45 schrieb Jan Trippler:
Sie wird expandiert, aber es ist nicht die Kommandozeile - die Begrenzung schlägt hier nicht zu. Die 128k Zeichen limitieren die Länge der Komandozeilenargumente.
Tatsächlich. Man lernt nie aus. Die Beschräkung greift wohl nicht mehr, weil "for" ein bash-internes Kommando ist, für das die Argumentliste auch nur bash-intern erstellt werden muss? MfG, Dennis -- Dennis Stosberg eMail: dennis@stosberg.net gpg key: http://stosberg.net/dennis.asc icq: 63537718
* Jürgen Hein schrieb am 12.Nov.2002:
wenn ich mit grep in Verzeichnissen mit sehr vielen Dateien, z.B. meinem News-Spool, suchen lassen will, bekomme ich immer die Fehlermeldung "Die Argumentliste ist zu lang".
Wißt Ihr dafür 'n "Gegenmittel"?
Ist doch wohl klar, kürzere Argumentliste. Dabei ist zu beachten, daß die shell etwaige Wildcards wie * ersetzt. Wenn Du z.B ein Verzeichnis hast mit $ ls bar baz foo foobar dann macht die shell aus $ grep irgendwas * grep irgendwas bar baz foo foobar grep hat keine Chance zu erkennen, daß Du ursprünglich nur ein * eingegeben hast. Für grep ist es das gleiche, als wenn Du gleich $ grep irgendwas bar baz foo foobar eingegeben hättest. Das kann man sogar soweit treiben, wenn Du $ grep f* sagst, dann macht die shell daraus: grep foo foobar es wird somit in der Datei foobar nach dem String foo gesucht. Die shell interpretiert nicht, sie ersetzt nur. Natürlich nur mit Dateien, die es im Verzeichnis gibt. Wenn es nun sehr viele Dateien sind, die auf dem Muster mit * zutreffen, dann kann es passieren, daß die Zeile zu lang wird. Die Länge ist aber begrenzt. Es kommt zu der Fehlermeldung. Also mußt Du was aufteilen. Es gibt verschiedene Möglichkeiten, etwa: find . -type f -exec grep -H SUCHWORT {} \; hat den Nachteil, daß für jede einzelne Datei grep angeschmissen wird. Bei so langen Listen wird es wohl langsam. Das -H von grep bewirkt, daß auch der Dateiname mit ausgegeben wird. Ohne -H würde nur der gefundene String ausgegeben. Damit wirst Du wahrscheinlich nicht viel anfangen können. Wenn Du nur die Datei wissen willst, aber nicht den String, so kanst Du anstelle des -H ein -l setzen. Eine andere Möglichkeit ist die Zusätzliche Verwendung von xargs find . -type f -print0 | xargs -0 grep SUCHWORT funktioniert wunderbar, nur Du wirst die gleiche Probleme haben, wie bisher, da xargs wieder eine genausolange Zeile baut. Das kann man aber auch umgehen. Dazu must Du mal man xargs lesen. Ist bei mir aber nicht sonderlich ergiebig. Vielleicht gibt es noch weitere Dokumentation. info brachte aber nichts, in /usr/share/doc/packages stand auch nichts und xargs --help ist denkbar knapp. Das -0 im Zusammenhang mit dem -print0 von find bewirkt, daß als Trenner zwichen den Namen nicht das Leerzeichen genommen wird, sondern das ASCII-Zeichen mit dem Wert 0. Also nicht das Zeichen 0, das den Dezimalwert 48, bzw. Hexadezimalwert 30 hat sondern das Zeichen NUL mit dem (Hexa)Dezimalwert 0. Das Ganze deshalb, weil es sonst zu Problemen kommt, wenn es Dateien gibt, die in ihren Namen ein Leerzeichen gibt. Eine einfache andere Möglichkeit wäre: grep -r Suchwort . Hier ist es grep, daß rekursiv sucht. Ich habe es aber nicht ausprobiert, wie es auf Riesenverzeichnisse wirkt, da ich keine solche habe. Es wird dann auch in allen Unterverzeichnissen gesucht, und es gibt Probleme, wenn es darin nicht nur Normale Dateien und Verzeichnisse gibt. Bei einer named Pipe (FIFO) bleibt es auf jeden Fall hängen, bei symlinks kann es passieren, daß es deadloops gibt. Eine einfache Möglichkeit ist natürlich den * einzuschränken. Also etwa a* und dann b* usw. bzw. 1* und dann 2* usw. Bernd -- ROTFL = Rolling On The Floor, Laughing = Auf dem Boden wälzen, lachend. SCNR = Sorry, Could Not Resist = Sorry, Ich konte nicht wiederstehen. AFAIK = As Far As I Know = So weit ich weis|BTW = By The Way = Nebenbei bemerkt IMHO = In My Humble Opinion = meiner bescheidenen Meinung nach |Zufallssig. 9
Hallo, On Tue, 12 Nov 2002, Bernd Brodesser wrote:
find . -type f -print0 | xargs -0 grep SUCHWORT
funktioniert wunderbar, nur Du wirst die gleiche Probleme haben, wie bisher, da xargs wieder eine genausolange Zeile baut.
Nein. Das ist ja die Hauptaufgabe von xargs, das wirft dem Befehl die Argumente "paeckchenweise" vor, per default ist lt. manpage 80k Zeichen. Steuern laesst sich das via --max-lines=, --max-args= und --max-chars= als Optionen. Mit '| xargs -0 -n 1 grep SUCHWORD' wuerde wieder fuer jede Datei ein eigenes grep angeworfen. Nuetzlich z.B. bei Befehlen wie dirname oder basename, die max. ein Argument wollen: $ find $PWD -type f | xargs dirname dirname: too many arguments [..] $ find $PWD -type f | xargs -n1 dirname [liste] -dnh -- An NT server can be run by an idiot, and usually is. -- Tom Holub, a.h.b-o-i
* David Haller schrieb am 13.Nov.2002:
On Tue, 12 Nov 2002, Bernd Brodesser wrote:
find . -type f -print0 | xargs -0 grep SUCHWORT
funktioniert wunderbar, nur Du wirst die gleiche Probleme haben, wie bisher, da xargs wieder eine genausolange Zeile baut.
Nein. Das ist ja die Hauptaufgabe von xargs, das wirft dem Befehl die Argumente "paeckchenweise" vor, per default ist lt. manpage 80k Zeichen. Steuern laesst sich das via --max-lines=, --max-args= und --max-chars= als Optionen.
Genau, die meinte ich. Das es ein Defaultwert gibt, wußte ich allerdings nicht. Ich dachte immer, das der Default wäre so viel wie gerade kommt. Hauptaufgabe von xargs ist aber erst mal die Möglichkeit zu schaffen, Daten, die über eine Pipe kommen als Argumente eines Befehls zu nehmen.
Mit '| xargs -0 -n 1 grep SUCHWORD' wuerde wieder fuer jede Datei ein eigenes grep angeworfen.
Was heißt wieder? Vorher gab es nur ein grep, aber da war die Zeile zu lang. Bernd -- Bitte die Etikette beachten: http://www.suse-etikette.de.vu/etikette.html Bitte Realnamen angeben, kein Vollquoting, kein Html, PGP oder Visitenkarten benutzen. Signatur mit "-- " abtrennen, bei Antworten "Re: " voranstellen, sonst nichts. |Zufallssignatur 4
Hallo, On Sat, 16 Nov 2002, Bernd Brodesser wrote:
* David Haller schrieb am 13.Nov.2002:
On Tue, 12 Nov 2002, Bernd Brodesser wrote:
find . -type f -print0 | xargs -0 grep SUCHWORT
funktioniert wunderbar, nur Du wirst die gleiche Probleme haben, wie bisher, da xargs wieder eine genausolange Zeile baut. [..] Mit '| xargs -0 -n 1 grep SUCHWORD' wuerde wieder fuer jede Datei ein eigenes grep angeworfen.
Was heißt wieder? Vorher gab es nur ein grep, aber da war die Zeile zu lang.
Das ist equivalent zu: 'find ... -exec grep 'SUCHWORT' {} \;', d.h. das xargs ist (bei dieser Kombination mit find) de facto ueberfluessig. Pro Datei wird ein grep angeworfen. Anders aber z.B. bei einem: find ... -print0 | xargs -0 -n 10 grep 'SUCHWORT' da wird nur ein grep, naemlich jew. ein: grep 'SUCHWORT' datei1 .. datei10 pro alle 10 Dateien angeworfen... Per default, d.h. xargs ohne '-n <irgendwas>' sind's i.d.R. (abhaengig von der Laenge der Dateinamen) deutlich mehr Dateien, fuer die xargs jew. ein grep anwirft, eben soviele Dateiennamen, die eben noch in die Kommandozeile (bzw. --max-chars) passen... Vergleiche: $ find . -type f -maxdepth 1 -print | wc -l 1441 $ find . -type f -maxdepth 1 -print0 | xargs -0 echo "echoX" | grep -c "echoX" 2 $ find . -type f -maxdepth 1 -print0 | xargs -0 -n 10 echo "echoX" | grep -c "echoX" 145 $ find . -type f -maxdepth 1 -print0 | xargs -0 -n 1 echo "echoX" | grep -c "echoX" 1441 (nein, eine Datei namens 'echoX' hab ich nicht im Verzeichnis, das 'echoX' ist jew. das erste Argument fuer das 'echo' das von xargs aufgerufen wird, traegt also zur "Laenge der Argumentliste fuer 'echo'" bei (und kann also bei obigem ggfs. das Ergebnis leicht verfaelschen, das 'echoX' dient schlicht als "Marker" dafuer, wie oft das 'echo' von 'xargs' aufgerufen wird))... -dnh -- Aaaargh. Was war das denn? Schiss-Dur, oder Gugge-Moll? So schief gestrichen hat ja noch keiner. -- Jakob in dag°
* David Haller schrieb am 16.Nov.2002:
On Sat, 16 Nov 2002, Bernd Brodesser wrote:
* David Haller schrieb am 13.Nov.2002:
On Tue, 12 Nov 2002, Bernd Brodesser wrote:
find . -type f -print0 | xargs -0 grep SUCHWORT
funktioniert wunderbar, nur Du wirst die gleiche Probleme haben, wie bisher, da xargs wieder eine genausolange Zeile baut.
Mit '| xargs -0 -n 1 grep SUCHWORD' wuerde wieder fuer jede Datei ein eigenes grep angeworfen.
Was heißt wieder? Vorher gab es nur ein grep, aber da war die Zeile zu lang.
Das ist equivalent zu: 'find ... -exec grep 'SUCHWORT' {} \;', d.h. das xargs ist (bei dieser Kombination mit find) de facto ueberfluessig.
Ja, aber aber der Ursprüngliche Befehl lautete: grep SUCHWORT * Und da war das Argument zu lang. Bernd -- ACK = ACKnowledge = Zustimmung | NAC = No ACknowledge = keine Zustimmung DAU = Dümmster Anzunehmender User | LOL = Laughing Out Loud = Lautes Lachen IIRC = If I Remember Correctly = Falls ich mich richtig erinnere OT = Off Topic = Am Thema (der Liste) vorbei |Zufallssignatur 11
On Sam, 16 Nov 2002 at 03:03 (+0100), David Haller wrote:
On Sat, 16 Nov 2002, Bernd Brodesser wrote:
* David Haller schrieb am 13.Nov.2002:
On Tue, 12 Nov 2002, Bernd Brodesser wrote:
find . -type f -print0 | xargs -0 grep SUCHWORT
funktioniert wunderbar, nur Du wirst die gleiche Probleme haben, wie bisher, da xargs wieder eine genausolange Zeile baut. [..] Mit '| xargs -0 -n 1 grep SUCHWORD' wuerde wieder fuer jede Datei ein eigenes grep angeworfen.
Was heißt wieder? Vorher gab es nur ein grep, aber da war die Zeile zu lang.
Das ist equivalent zu: 'find ... -exec grep 'SUCHWORT' {} \;', d.h. das xargs ist (bei dieser Kombination mit find) de facto ueberfluessig.
Pro Datei wird ein grep angeworfen. Anders aber z.B. bei einem:
find ... -print0 | xargs -0 -n 10 grep 'SUCHWORT'
da wird nur ein grep, naemlich jew. ein:
grep 'SUCHWORT' datei1 .. datei10
pro alle 10 Dateien angeworfen...
Du denkst aber dran, dass gerade grep einen Unterschied macht (Ausgabe des Dateinamens), je nachdem wie viele Dateien er bearbeitet. Jan
Am Dienstag, 12. November 2002 20:31 schrieb Bernd Brodesser: Moin moin Bernd, erstmal: Meine/n Anerkennung und besonderen Dank an Dich für diese Deine jedenfalls nach meinem Verständnis in Form *und* Inhalt *vorbildliche* Hilfe!
wenn ich mit grep in Verzeichnissen mit sehr vielen Dateien, z.B. meinem News-Spool, suchen lassen will, bekomme ich immer die Fehlermeldung "Die Argumentliste ist zu lang".
Wißt Ihr dafür 'n "Gegenmittel"?
Ist doch wohl klar, kürzere Argumentliste.
Ja ja, sicher, aber wieso denn, hatte doch nur...
Dabei ist zu beachten, daß die shell etwaige Wildcards wie * ersetzt.
...jo, genau, ein * hintendrangemacht? ;-))
Wenn Du z.B ein Verzeichnis hast mit
$ ls
bar baz foo foobar
Na ja, wie gesagt, es geht (hauptsächlich) um meinen News-Cache 'meines' Newsreaders 'Pan', was ein (Unter)Verzeichnis, ~/../.pan/messages/cache, dies selbst jedoch ohne (weitere) Unterverzeichnisse, ist, in dem letzterer alle kompletten Postings (die mit Body's), momentan 9264 Stück, mit den Message-ID's als, also ziemlich langen, Dateinamen, ablegt. Und in dem ich immer mal was zu suchen habe. Und zwar entweder, weil der Reader (noch) keine Volltextsuche kann, nach bestimmten Textteilen. Oder, um den Cache zu aktualisieren gleich auszudünnen - wegen der sonst ziemlich lang werdenden Ladezeit - (z.B.) über den Date-Header nach bestimmten Postings gleich Dateie(namen)n, um die dann löschen...wozu sich nun noch 'ne 'Zusatzfrage' ;-) ...->
dann macht die shell aus
$ grep irgendwas *
grep irgendwas bar baz foo foobar
grep hat keine Chance zu erkennen, daß Du ursprünglich nur ein * eingegeben hast. Für grep ist es das gleiche, als wenn Du gleich
$ grep irgendwas bar baz foo foobar
eingegeben hättest. Das kann man sogar soweit treiben, wenn Du
$ grep f*
sagst, dann macht die shell daraus:
grep foo foobar
es wird somit in der Datei foobar nach dem String foo gesucht. Die shell interpretiert nicht, sie ersetzt nur. Natürlich nur mit Dateien, die es im Verzeichnis gibt.
Wenn es nun sehr viele Dateien sind, die auf dem Muster mit * zutreffen, dann kann es passieren, daß die Zeile zu lang wird. Die Länge ist aber begrenzt. Es kommt zu der Fehlermeldung.
...ok., denke mal, dass ich das jetzt zumindest 'einigermassen' :-) verstanden habe....
Also mußt Du was aufteilen. Es gibt verschiedene Möglichkeiten, etwa:
find . -type f -exec grep -H SUCHWORT {} \;
hat den Nachteil, daß für jede einzelne Datei grep angeschmissen wird. Bei so langen Listen wird es wohl langsam. Das -H von grep bewirkt, daß auch der Dateiname mit ausgegeben wird. Ohne -H würde nur der gefundene String ausgegeben. Damit wirst Du wahrscheinlich nicht viel anfangen können.
Wenn Du nur die Datei wissen willst, aber nicht den String, so kanst Du anstelle des -H ein -l setzen.
...jou! Genau das war's, äh, bzw. damit habe ich's nun 'immerhin' schon mal geschafft, die Dateinamen auf den Bildschirm zu bekommen. Aber!, von wegen "Zusatzfrage": Wie kann ich diesen Befehl jetzt noch erweitern, die Dateien auch gleich in ein anderes Verzeichnis verschoben oder 'wenigstens' kopiert zu kriegen?? Hab's mit 'ner Pipe, also | cp -a /path noch hintendran, probiert, geht aber leider nicht. :-(
Eine andere Möglichkeit ist die Zusätzliche Verwendung von xargs
find . -type f -print0 | xargs -0 grep SUCHWORT
funktioniert wunderbar, nur Du wirst die gleiche Probleme haben, wie bisher, da xargs wieder eine genausolange Zeile baut. Das kann man aber auch umgehen. Dazu must Du mal man xargs lesen. Ist bei mir aber nicht sonderlich ergiebig. Vielleicht gibt es noch weitere Dokumentation. info brachte aber nichts, in /usr/share/doc/packages stand auch nichts und xargs --help ist denkbar knapp.
Hm, fürchte, das ist...
Das -0 im Zusammenhang mit dem -print0 von find bewirkt, daß als Trenner zwichen den Namen nicht das Leerzeichen genommen wird, sondern das ASCII-Zeichen mit dem Wert 0. Also nicht das Zeichen 0, das den Dezimalwert 48, bzw. Hexadezimalwert 30 hat sondern das Zeichen NUL mit dem (Hexa)Dezimalwert 0. Das Ganze deshalb, weil es sonst zu Problemen kommt, wenn es Dateien gibt, die in ihren Namen ein Leerzeichen gibt.
...(sowieso) 'zu hoch' für mich ja nur Amateur.
Eine einfache andere Möglichkeit wäre:
grep -r Suchwort .
Hier ist es grep, daß rekursiv sucht. Ich habe es aber nicht ausprobiert, wie es auf Riesenverzeichnisse wirkt, da ich keine solche habe.
Es wird dann auch in allen Unterverzeichnissen gesucht, und es gibt Probleme, wenn es darin nicht nur Normale Dateien und Verzeichnisse gibt. Bei einer named Pipe (FIFO) bleibt es auf jeden Fall hängen, bei symlinks kann es passieren, daß es deadloops gibt.
Tja, bringt hier jedenfalls wieder nur "Die Argumentliste ist zu lang"
Eine einfache Möglichkeit ist natürlich den * einzuschränken. Also etwa a* und dann b* usw. bzw. 1* und dann 2* usw.
Na ja, ziemlich umständlich, ne'? Jedenfalls wär's schon schön, wenn's doch noch 'ne bequemere...in einem Rutsch... :) Auf jeden Fall aber noch einmal vielen Dank für Deine bisher ja schon grosse Mühe...!,
Bernd
Grüsse und tschö Jürgen -- Wer das erste Knopfloch verfehlt, kommt mit dem Zuknöpfen nicht zurande. Goethe
* Jürgen Hein schrieb am 14.Nov.2002:
Am Dienstag, 12. November 2002 20:31 schrieb Bernd Brodesser:
Also mußt Du was aufteilen. Es gibt verschiedene Möglichkeiten, etwa:
find . -type f -exec grep -H SUCHWORT {} \;
...jou! Genau das war's, äh, bzw. damit habe ich's nun 'immerhin' schon mal geschafft, die Dateinamen auf den Bildschirm zu bekommen. Aber!, von wegen "Zusatzfrage": Wie kann ich diesen Befehl jetzt noch erweitern, die Dateien auch gleich in ein anderes Verzeichnis verschoben oder 'wenigstens' kopiert zu kriegen?? Hab's mit 'ner Pipe, also | cp -a /path noch hintendran, probiert, geht aber leider nicht. :-(
Nein, cp ließt nicht von der Standardeingabe. Die Pipe | macht ja nichts anderes, als die Standardausgabe des einen Befehls zur Standardeingabe des nächsten zu machen, aber cp ignoriert die Standardeingabe, es kann somit nichts daraus werden. Wenn Du da noch eine Aktion machen willst, so kannst Du das mit find machen. Dazu mußt Du verstehen, wie find vorgeht. Laßen wir dazu mal -o bzw. -or das oder von find beiseite. Ein typischer find Befehl könnte so aussehen: find . -type f ! -user root -size +2k -mtime +3 Es werden alle Dateien im aktuellen Verzeichnis oder eines der Unterverzeichnisse gesucht, wegen des . Anstelle des . könnte jedes andere Verzeichnis stehen. Der . steht aber für das aktuelle Verzeichnis. Jetzt wird jede Datei durch diese Kette geschleust. Zuerst wird der Typ überprüft. Nur reguläre Dateien werden durchgelassen, also keine Verzeichnisse, keien Gerätedateien, keine FIFOs. Das macht das -type f Als nächstes wird der User überprüft. Das ! davor ist die Negation. Es werden nur Dateien weitergeführt, die nicht root gehören. Als nächstes nur Dateien, die größer sind als 2k und schließlich solche, die zuletzt vor mehr als drei Tagen verändert wurden. Dabei sind keien Kalendertage gemeint, sondern 24 Stunden, oder 1440 Minuten, oder 86400 Sekunden seit jetzt. Hier natürlich alles mal 3 ;) Nur eine Datei, die das alles erfüllt, wird am Ende angezeigt. Eine Datei, die ein Kriterium nicht erfüllt, wird vorher ausgesondert, und das nächste Kriterium wird erst gar nicht mehr überprüft. Das kanst Du mit zwichengestreuten -print, oder besser -printf überprüfen. Wenn in der Kette ein -exec auftaucht, so wird das hier angegebene Kommando ausgeführt. Das Kommando wird mit einem ; beendet. Allerdings interpretiert die shell, die das ganze ja interpretiert das ; deshalb muß es geschützt werden. Würdest Du find nicht von der shell aus aufrufen, sondern etwa aus einem C-Programm heraus, so müßte da zwar immer noch ein ; stehen, aber kein \ davor. Innerhalb des -exec ... ; wird ein auftretendes {} durch den Dateinamen ersetzt. Das wird ja z.B mit dem grep gemacht. Aber so ein -exec dient nicht nur der Ausgabe, sondern auch der Flußkontrolle. Jeder Befehl hat ein Rückgabewert, auch Exitstatus genannt. Es ist ein Integerwert. Es wird 0 als wahr und alles andere als falsch interpretiert. Speziell bei grep ist es so, daß grep eine 0 zurückgibt, wenn es was gefunden hat, eine 1 wenn es nichts gefunden hat, und eine 2, wenn ein Syntaxfehler vorlag. Das heißt, wenn nach dem -exec grep ... \; noch was anderes im find kommt, dann wird das nur ausgeführt, wenn grep erfolgreich war. Etwa so: find -type f -exec grep -q {} \; -exec cp {} NEUES_VERZEICHNIS \; Das -q im grep Befehl bewirkt, daß überhaupt keine Ausgabe gemacht wird, es kommt nur auf dem Exitstatus an. Aber nur, wenn was gefunden wurde, geht es weiter und es kommt zum nächsten -exec und hier wird kopiert. Wenn Du statt des cp ein mv schreibst, dann wird nicht kopiert, sondern verschoben. Aber da wäre ich erstmal vorsichtig mit, wenn die Dateien weg sind, sind sie weg. Allerdings werden hier sehr viele Prozesse gestartet, und das kann alles sehr lange dauern. Man könnte auch mit xargs arbeiten, aber dann hast Du wieder das Problem mit überlangen Zeilen. Bernd -- Alle meine Signaturen sind rein zufällig und haben nichts mit dem Text oder dem Schreiber zu tun, dem ich antworte. Falls irgendwelche Unrichtigkeiten dabei sein sollten, so bedauere ich das. Es wäre nett, wenn Du mich benachrichtigen würdest. |Zufallssignatur 0
Am Donnerstag, 14. November 2002 18:37 schrieb Bernd Brodesser: Moin Bernd, schon wieder, ;-)
[...] Aber!, von wegen "Zusatzfrage": Wie kann ich diesen Befehl jetzt noch erweitern, die Dateien auch gleich in ein anderes Verzeichnis verschoben oder 'wenigstens' kopiert zu kriegen?? Hab's mit 'ner Pipe, also | cp -a /path noch hintendran, probiert, geht aber leider nicht. :-(
Nein, cp ließt nicht von der Standardeingabe. Die Pipe | macht ja nichts anderes, als die Standardausgabe des einen Befehls zur Standardeingabe des nächsten zu machen, aber cp ignoriert die Standardeingabe, es kann somit nichts daraus werden.
Aha - tja dann - ;-)
Wenn Du da noch eine Aktion machen willst, so kannst Du das mit find machen. Dazu mußt Du verstehen, wie find vorgeht. Laßen wir dazu mal -o bzw. -or das oder von find beiseite. Ein typischer find Befehl könnte so aussehen:
find . -type f ! -user root -size +2k -mtime +3
Ui juijuijui, wat kryptisch! ;-)
Es werden alle Dateien im aktuellen Verzeichnis oder eines der Unterverzeichnisse gesucht, wegen des . Anstelle des . könnte jedes andere Verzeichnis stehen. Der . steht aber für das aktuelle Verzeichnis.
Ach so, na ja, das...
Jetzt wird jede Datei durch diese Kette geschleust. Zuerst wird der Typ überprüft. Nur reguläre Dateien werden durchgelassen, also keine Verzeichnisse, keien Gerätedateien, keine FIFOs. Das macht das -type f
...auch...
Als nächstes wird der User überprüft. Das ! davor ist die Negation. Es werden nur Dateien weitergeführt, die nicht root gehören. Als nächstes nur Dateien, die größer sind als 2k und schließlich solche, die zuletzt vor mehr als drei Tagen verändert wurden. Dabei sind keien Kalendertage gemeint, sondern 24 Stunden, oder 1440 Minuten, oder 86400 Sekunden seit jetzt. Hier natürlich alles mal 3 ;)
Nur eine Datei, die das alles erfüllt, wird am Ende angezeigt. Eine Datei, die ein Kriterium nicht erfüllt, wird vorher ausgesondert, und das nächste Kriterium wird erst gar nicht mehr überprüft. Das kanst Du mit zwichengestreuten -print, oder besser -printf überprüfen.
...jo, kann ich so ja nun doch verstehen, prima! :-)
Wenn in der Kette ein -exec auftaucht, so wird das hier angegebene Kommando ausgeführt. Das Kommando wird mit einem ; beendet. Allerdings interpretiert die shell, die das ganze ja interpretiert das ; deshalb muß es geschützt werden. Würdest Du find nicht von der shell aus aufrufen, sondern etwa aus einem C-Programm heraus, so müßte da zwar immer noch ein ; stehen, aber kein \ davor. Innerhalb des -exec ... ; wird ein auftretendes {} durch den Dateinamen ersetzt. Das wird ja z.B mit dem grep gemacht.
Auch gebont,...
Aber so ein -exec dient nicht nur der Ausgabe, sondern auch der Flußkontrolle. Jeder Befehl hat ein Rückgabewert, auch Exitstatus genannt. Es ist ein Integerwert. Es wird 0 als wahr und alles andere als falsch interpretiert. Speziell bei grep ist es so, daß grep eine 0 zurückgibt, wenn es was gefunden hat, eine 1 wenn es nichts gefunden hat, und eine 2, wenn ein Syntaxfehler vorlag.
Das heißt, wenn nach dem -exec grep ... \; noch was anderes im find kommt, dann wird das nur ausgeführt, wenn grep erfolgreich war. Etwa so:
...dito...
find -type f -exec grep -q {} \; -exec cp {} NEUES_VERZEICHNIS \;
...<gleichmalprobieren> - jaa, funzt doch hundertpro! Dufte! :)
Das -q im grep Befehl bewirkt, daß überhaupt keine Ausgabe gemacht wird, es kommt nur auf dem Exitstatus an. Aber nur, wenn was gefunden wurde, geht es weiter und es kommt zum nächsten -exec und hier wird kopiert.
Äh - also das ist jetzt das einzige, was ich noch nicht verstehe, wie cp denn so von den Dateinamen 'erfährt, erfahren' kann -?
Wenn Du statt des cp ein mv schreibst, dann wird nicht kopiert, sondern verschoben. Aber da wäre ich erstmal vorsichtig mit, wenn die Dateien weg sind, sind sie weg.
Ja, ist klar, hab's aber just doch mal riskiert und auch probiert, und, ja, fluppt auch ganz prima.
Allerdings werden hier sehr viele Prozesse gestartet, und das kann alles sehr lange dauern.
Nö, doch gar nich! :) Hier jedenfalls geht das ganz erheeeblich schneller als mit Konqueror per "Dateien suchen" und "...kopieren/verschieben"!
Man könnte auch mit xargs arbeiten, aber dann hast Du wieder das Problem mit überlangen Zeilen.
Na, dann lass' ich das doch einfach. :) Sowieso, wo ich doch nun alles habe, was ich wollte! :) Dank Deiner wieder ganz ausgezeichneten 'Beratung',
Bernd
für die ich mich natürlich wieder bzw. noch einmal gern bei Dir bedanke! Und tschö Jürgen -- Wer das erste Knopfloch verfehlt, kommt mit dem Zuknöpfen nicht zurande. Goethe
* Jürgen Hein schrieb am 15.Nov.2002:
Am Donnerstag, 14. November 2002 18:37 schrieb Bernd Brodesser:
find -type f -exec grep -q {} \; -exec cp {} NEUES_VERZEICHNIS \;
...<gleichmalprobieren> - jaa, funzt doch hundertpro! Dufte! :)
Das -q im grep Befehl bewirkt, daß überhaupt keine Ausgabe gemacht wird, es kommt nur auf dem Exitstatus an. Aber nur, wenn was gefunden wurde, geht es weiter und es kommt zum nächsten -exec und hier wird kopiert.
Äh - also das ist jetzt das einzige, was ich noch nicht verstehe, wie cp denn so von den Dateinamen 'erfährt, erfahren' kann -?
Sehen wir uns doch mal die Regel -exec cp {} NEUES_VERZEICHNIS \; an. Die shell macht daraus -exec cp {} NEUES_VERZEICHNIS ; Sie nimmt den Backslash weg, läßt dafür aber den ; durch. Und so sieht es find. find nun wieder kommt ja mit einer Datei an, die die bisherigen Filter erfolgreich überlebt hat. Nun wird der Dateiname anstelle von {} gesetzt und so cp übergeben. Angenommen die Datei heißt foo, dann sieht cp folgendes: cp foo NEUES_VERZEICHNIS und cp kopiert in NEUES_VERZEICHNIS wenn als nächstes die Datei bar heißt, dann setzt find anstelle des {} bar ein und der cp Befehl lautet: cp bar NEUES_VERZEICHNIS Was ich noch vergessen habe. Wichtig, daß das Verzeichnis NEUES_VERZEICHNIS bereits existiert. Sonst nimmt cp NEUES_VERZEICHNIS als Dateiname. Nicht ganz das, was gewollt ist. Vorallem, weil dann diese Datei jedesmal überschrieben wird.
Allerdings werden hier sehr viele Prozesse gestartet, und das kann alles sehr lange dauern.
Nö, doch gar nich! :) Hier jedenfalls geht das ganz erheeeblich schneller als mit Konqueror per "Dateien suchen" und "...kopieren/verschieben"!
Nun gut, jede einzel von Hand kopieren dauert wohl noch länger. Bernd -- ACK = ACKnowledge = Zustimmung | NAC = No ACknowledge = keine Zustimmung DAU = Dümmster Anzunehmender User | LOL = Laughing Out Loud = Lautes Lachen IIRC = If I Remember Correctly = Falls ich mich richtig erinnere OT = Off Topic = Am Thema (der Liste) vorbei |Zufallssignatur 11
hallo jürgen,
werdenden Ladezeit - (z.B.) über den Date-Header nach bestimmten Postings gleich Dateie(namen)n, um die dann löschen...wozu sich nun
der einfachste weg, um rekursiv (ohne das parameterlisten-problem;) nach einem text in einer datei zu suchen, ist: find . -type f | xargs grep "SUCHBEGRIFF"
von wegen "Zusatzfrage": Wie kann ich diesen Befehl jetzt noch erweitern, die Dateien auch gleich in ein anderes Verzeichnis verschoben oder 'wenigstens' kopiert zu kriegen?? Hab's mit 'ner Pipe, also | cp -a /path noch hintendran, probiert, geht aber leider nicht. :-(
willst du die datei gleich noch kopieren, dann machst: find . -type f | cp `xargs grep -l "SUCHBEGRIFF"` /home/ziel/zieldatei achte aber darauf, dass den command genau so eingibst... ausser deine gewünschten suchbegriffe und zieldateien. bei grep ist das -l ein minus ell. und es müssen back-tics sein beim cp command. das bewirkt, dass dieser befehl zuerst ausgeführt wird, und das ergebnis cp übergeben wird. kannst auch durch mv (move,ersetzen) ersetzen, hehe.
Das -0 im Zusammenhang mit dem -print0 von find bewirkt, daß als Trenner zwichen den Namen nicht das Leerzeichen genommen wird, sondern das ASCII-Zeichen mit dem Wert 0. Also nicht das Zeichen 0, das den Dezimalwert 48, bzw. Hexadezimalwert 30 hat sondern das Zeichen NUL mit dem (Hexa)Dezimalwert 0. Das Ganze deshalb, weil es sonst zu Problemen kommt, wenn es Dateien gibt, die in ihren Namen ein Leerzeichen gibt.
ist aber irrelevant wenn man den suchbegriff in double- (" ") oder single(' ') quotes setzt. bei " " werden allerdings einige shellmetazeichen (* $ usw) ausgelesen, bei ' ' nicht, da werden diese zeichen auch als reiner text behandelt.
Es wird dann auch in allen Unterverzeichnissen gesucht, und es gibt Probleme, wenn es darin nicht nur Normale Dateien und Verzeichnisse gibt. Bei einer named Pipe (FIFO) bleibt es auf jeden Fall hängen, bei symlinks kann es passieren, daß es deadloops gibt.
deswegen, bei find immer 'type -f' sucht nur dateien, oder 'type -d'
directories.
hoff dies hilft dir nun richtig geile scripts zu hacken, hehe. meinen ftp verwalte ich auch komplett script gesteuert.
have phun & greetz us dr schwiitz
miLosh
--
this message was send by:
+------------------------------+
|.::|[ The pleXus Network ]|::.|
+------------------------------+
www: plexus.shacknet.nu |
ftp: plexus.shacknet.nu +-------------------+
irc: plexus.shacknet.nu:6667 channel: #nocturne |
+--------------------------------------------------+-------------+
pub 2048/9ABF6F59 2002/11/03 miLosh
On Don, 14 Nov 2002 at 14:29 (+0100), Jürgen Hein wrote: [...]
..jou! Genau das war's, äh, bzw. damit habe ich's nun 'immerhin' schon mal geschafft, die Dateinamen auf den Bildschirm zu bekommen. Aber!, von wegen "Zusatzfrage": Wie kann ich diesen Befehl jetzt noch erweitern, die Dateien auch gleich in ein anderes Verzeichnis verschoben oder 'wenigstens' kopiert zu kriegen?? Hab's mit 'ner Pipe, also | cp -a /path noch hintendran, probiert, geht aber leider nicht. :-(
Hast Du mal die Möglichkeit einer for-Schleife ins Auge gefasst? Da kannst Du beliebig viele Befehle pro Datei absetzen (hatte ich schon gezeigt): for i in *; do befehl_1 befehl_2 ... done Jan
Am Donnerstag, 14. November 2002 20:50 schrieb Jan Trippler: Moin moin Jan, danke gern auch Dir für Deine Antwort(en). Allerdings,...
..jou! Genau das war's, äh, bzw. damit habe ich's nun 'immerhin' schon mal geschafft, die Dateinamen auf den Bildschirm zu bekommen. Aber!, von wegen "Zusatzfrage": Wie kann ich diesen Befehl jetzt noch erweitern, die Dateien auch gleich in ein anderes Verzeichnis verschoben oder 'wenigstens' kopiert zu kriegen?? Hab's mit 'ner Pipe, also | cp -a /path noch hintendran, probiert, geht aber leider nicht. :-(
Hast Du mal die Möglichkeit einer for-Schleife ins Auge gefasst?
...nein, denn ich weiß kaum, was das ist.
Da kannst Du beliebig viele Befehle pro Datei absetzen (hatte ich schon gezeigt):
Na ja, "gezeigt" ist gut, wenn man, wie ja gesagt, Amateur ist und keine Ahnung von (Shell-)Programmierung hat. ;-)( Was aber nicht heißt, dass (auch mir) nicht interessant erschiene, "beliebig viele Befehle...", aber was heißt denn "pro Datei"? Etwa für jede der ja über 9000 (mindestens) einen?!
for i in *; do befehl_1 befehl_2 ... done
Jo, wie jesacht, sieht 'irgendwie interessant' aus, aber wo und wie muß ich denn da nun konkret die Befehle, Pfade und den Suchstring 'beischreiben'? :-)
Jan
Und tschö Jürgen -- Wer das erste Knopfloch verfehlt, kommt mit dem Zuknöpfen nicht zurande. Goethe
On Fre, 15 Nov 2002 at 17:18 (+0100), Jürgen Hein wrote:
Am Donnerstag, 14. November 2002 20:50 schrieb Jan Trippler:
Hast Du mal die Möglichkeit einer for-Schleife ins Auge gefasst?
..nein, denn ich weiß kaum, was das ist.
Da kannst Du beliebig viele Befehle pro Datei absetzen (hatte ich schon gezeigt):
Na ja, "gezeigt" ist gut, wenn man, wie ja gesagt, Amateur ist und keine Ahnung von (Shell-)Programmierung hat. ;-)( Was aber nicht heißt, dass (auch mir) nicht interessant erschiene, "beliebig viele Befehle...", aber was heißt denn "pro Datei"? Etwa für jede der ja über 9000 (mindestens) einen?!
for i in *; do befehl_1 befehl_2 ... done
Jo, wie jesacht, sieht 'irgendwie interessant' aus, aber wo und wie muß ich denn da nun konkret die Befehle, Pfade und den Suchstring 'beischreiben'? :-)
Kurzbeschreibung (eine ausführliche gibts in jedem guten Linux-Buch): Die Schleife sagt: Gehe über jede Datei (*) und tue damit befehl_1, befehl_2, ... In der Variablen $i wird nacheinander jeder Dateiname gespeichert. Für ein cp sieht das so aus: for i in *; do cp $i /pfad/zum/zielverzeichnis done Wenn Du z. B. erst eine Sicherheitskopie machen willst, anschließend einen Suchbegriff rausfiltern und zum Schluss die Dateien löschen willst, dann sieht das so aus: for i in *; do cp $i /pfad/zum/zielverzeichnis grep -l suchbegriff $i >logdatei rm $i done Jan
participants (6)
-
B.Brodesser@t-online.de
-
David Haller
-
Dennis Stosberg
-
Jan.Trippler@t-online.de
-
Jürgen Hein
-
milosh@plexus.shacknet.nu