wie grep große Anzahl von Dateien?
Ich versuche grep
40k Dateien im aktuellen Verzeichnis und ich bin immer diese Fehlermeldung.
for i in $(cat A01/genes.txt); do grep $i *.kaks; done > A01/A01.result.txt
-bash: /usr/bin/grep: Argument list too long
Wie kann man in der Regel grep
tausenden von Dateien?
Dank
Upendra
- Ich glaube, Sie verwenden
find
statt
Du musst angemeldet sein, um einen Kommentar abzugeben.
Diese macht David traurig...
Jeder, so weit ist es falsch (außer für anubhava).
Shell-scripting ist nicht wie jede andere Programmiersprache, weil viel von der interpretation der Linien kommt von der macht der shell-interpolierende Sie, bevor der Befehl tatsächlich ausgeführt wird.
Nehmen wir mal etwas einfaches:
Den
set -x
ermöglicht es Ihnen, um zu sehen, wie die shell tatsächlich interpoliert die glob und dann geht es zurück, um das Befehls als Eingabe. Die>
Punkte zu der Linie, die tatsächlich ausgeführt wird, indem Sie den Befehl.Können Sie sehen, dass die
echo
Befehl nicht interpretieren*
. Stattdessen die shell schnappt sich den*
und ersetzt es mit dem Namen der passenden Dateien. Dann und nur dann funktioniert dieecho
Befehl tatsächlich ausgeführt wird der Befehl.Wenn du 40K plus-Dateien, und Sie tun
grep *
Sie ständig, dass*
auf die Namen derer, die 40.000 plus-Dateien vorgrep
noch eine chance hat, zu führen, und das ist, wo die Fehlermeldung /usr/bin/grep: Argument-Liste zu lang kommt.Glücklicherweise Unix hat einen Weg, um dieses dilemma:
Den
find . -name "*.kaks" -type f -maxdepth 1
finden Sie alle Ihre*.kaks
Dateien, und die-depth 1
werden nur Dateien im aktuellen Verzeichnis. Die-type f
stellt sicher, dass Sie nur abholen, Dateien und kein Verzeichnis.Den
find
Befehl leitet die Namen der Dateien inxargs
undxargs
wird, fügen Sie den Namen der Datei an, diegrep -f A01/genes.txt
Befehl. Allerdingsxargs
hat einen trick Sie ärmel. Er weiß, wie lange der Befehlszeile Puffer ist, und führt diegrep
wenn die Befehlszeile Puffer voll ist, dann passieren in einer anderen Serie von Datei diegrep
. Diese Weisegrep
wird ausgeführt, vielleicht drei oder zehn mal (je nach Größe der Befehlszeile Puffer), und alle unsere Dateien verwendet werden.Leider
xargs
verwendet whitespace-Zeichen als ein Trennzeichen für die Datei-Namen. Wenn Sie Ihre Dateien mit Leerzeichen oder Tabulatoren, werden Sie Schwierigkeiten haben, mitxargs
. Glücklicherweise gibt es ein weiteres Update:Den
-print0
verursachenfind
drucken Sie die Namen der Dateien, die nicht durch Zeilenumbrüche getrennte, aber durch das Zeichen NUL. Die-0
parameter fürxargs
sagtxargs
dass die Datei separator ist nicht whitespace-Zeichen, sondern das Zeichen NUL. So, das Problem behebt.Könnte man auch dies auch tun:
Dies führt die
grep
für jeden und jede Datei statt gefunden, wasxargs
tut, und läuft nurgrep
für alle Dateien, die es können Sachen auf der Kommandozeile. Dies hat den Vorteil, daß es vermeidet shell Störungen vollständig. Jedoch, es möglicherweise nicht oder weniger effizient.Was auch interessant wäre ist, zu Experimentieren und sehen, welche effizienter ist. Sie können
time
zu sehen:Dies wird den Befehl ausführen und dann sagen Sie, wie lange es dauerte. Versuchen Sie es mit der
-exec
und mitxargs
und sehen, welche schneller ist. Lassen Sie uns wissen, was Sie suchen.-exec command {} +
form, zumindest auf den Systemen, die es unterstützen?+
form derfind
. Ich sah nie, dass vor. Ja, laut der manpage: genau wie -exec, nur dass `{}" wird durch so viele Pfade wie möglich mit jedem Aufruf der utility. Dieses Verhalten ist ähnlich der von xargs(1).. Es ist das, was passiert, wenn Sie lernen, die Dinge von vor 20 Jahren, und nicht halten mit den Veränderungen.xargs
Methode, die @DavidW. vorgeschlagen, und es funktionierte ziemlich schnelltime find . -name "*.kaks" -type f -maxdepth 1 | xargs grep -f A01/genes.txt > A01/A01.result.txt
real 4m36.566s user 2m2.835s sys 0m6.023s
Jedoch die andere Methode, die w/o -xargs
scheint es nicht zu sein, etwas zu tun (nicht füllen Sie die Ausgabe-Datei). Seit mehr als 20 min ohne Ausgang...Wahrscheinlich werde ich stick mit derxarg
Methode der nun. Danke @DavidW. wieder..for i in *.kah
. Tut Bash sehen die*.kah
und füllen Sie es mit allen Dateien, die entsprechen, dann führen Sie diefor
Schleife. Dies ist, wie Bourne und Kornshell betreiben. Wenn dem so ist, werden Sie am Ende eine überlastung der Befehlszeile in derfor
- Schleife nur als Sie würden, wenn Sie getan habengrep *.kah
. Wenn Bash betreibt diefor
auf eine andere Weise, es kann in Ordnung sein. Ich würde zu spielen, um mitfor
Schleifen auf Bash und sehen, wiefor
gefüllt wird in.Kombinieren Sie
find
mitgrep
wie diese:cat A01/genes.txt
auf der Kommandozeile, und es vermeidet, (b) die Erweiterung*.kaks
auf der Kommandozeile.können Sie rekursive Funktion
grep
:obwohl, wenn Sie möchten, wählen Sie nur
kaks
Dateien:Stellen eine weitere for-Schleife in Ihrem äußeren:
Durch die Art und Weise, sind Sie daran interessiert, JEDES vorkommen in jeder Datei, oder nur, wenn der Suchstring vorhanden ist, in der es ein oder mehrere Male? Wenn es "gut genug" um zu wissen, die Zeichenfolge Auftritt, in der es ein oder mehrere Male können Sie angeben, "- n 1" zum grep und es wird sich nicht die Mühe zu Lesen/suchen, der rest der Datei zu finden, nachdem das erste match, das könnte möglicherweise sparen viel Zeit.
Folgende Lösung hat für mich gearbeitet:
Problem:
Lösung:
["In neueren Versionen von grep kann man auch weglassen “.“, als Aktuelles Verzeichnis wird angedeutet."]
Quelle:
Reinlick, J. https://www.saotn.org/bash-grep-through-large-number-files-argument-list-too-long/