Ich bin neu im Windows-Ökosystem. Ich habe die Aufgabe, ein Programm zu schreiben, das mehrere Zehntausend (vielleicht sogar Hunderttausende) von Dateien nach einer bestimmten Zeichenfolge durchsucht. Die Zeichenfolge, die übereinstimmen muss, ist eine Seriennummer, die nur aus Zahlen und Buchstaben besteht und weniger als 20 Zeichen lang ist. Im Moment führt mein Programm den folgenden Befehl aus:
findstr /i /m /s "searchStr" "C:\Directory\To\Search\*.*"
Der obige Befehl funktioniert, ist jedoch zu langsam. Die Dateien, die eine bestimmte Seriennummer enthalten könnten, haben diese Seriennummer nur in der ersten Zeile.
Kennt jemand eine effiziente Möglichkeit, ein Verzeichnis rekursiv nach allen Dateien zu durchsuchen, die eine bestimmte Zeichenfolge nur in der ersten Zeile enthalten?
Antwort1
In PowerShell (v3.0+) vielleicht ...
Get-ChildItem -Path x:\pathto\*.log `
| ForEach-Object {
if (Get-Content -LiteralPath $_ -First 1 `
| Select-String -SimpleMatch -Pattern 'serialnumber')
{
Write-Output $_
}
}
Verschiedene Parameter Get-ChildItem
ermöglichen die Rekursion in Unterordnern usw., das Get-Content
Abrufen von mehr oder weniger Inhalt aus der Datei und das Select-String
Durchführen komplexerer Übereinstimmungen (reguläre Ausdrücke, Groß-/Kleinschreibung usw.).
Antwort2
Ich kann Ihnen ein paar Optionen vorschlagen, wenn Sie nicht verwenden müssen findstr
, aber zunächst sollten Sie prüfen, ob Sie die Suche auf Dateien eines bestimmten Dateityps beschränken können, da dies die Sache sicherlich beschleunigen würde.
FileLocator Liteist meiner Erfahrung nach schneller beim Auffinden von Dateien und Überprüfen ihres Inhalts. Achten Sie darauf, sowohl die Felder „Dateiname“ (falls zutreffend) und „enthaltener Text“ als auch das Startverzeichnis auszufüllen.
ag -il "searchStr"
:agist auf Geschwindigkeit ausgelegt und sollte Ihnen daher schnell Ergebnisse liefern. Achten Sie darauf, die Suche nach Dateityp einzuschränken, wenn Sie können, obwohl Binärdateien bereits standardmäßig übersprungen werden. Auch verfügbar unterCygwin.find -exec awk 'BEGIN {IGNORECASE=1} NR==1 && /searchStr/ {print FILENAME": "$0}' {} \;
Versuchen Sie dies, wenn Sie Cygwin oder eine andere POSIX-ähnliche Umgebung zur Verfügung haben, um Ihre Idee zu testen, nur die erste Zeile zu durchsuchen. Kombinieren Sie dies,find
um die Dateinamen abzurufen (und sie hoffentlich auch zu filtern) undawk
um die erste Zeile zu prüfen und sie zusammen mit dem Dateinamen auszudrucken.find | parallel 'perl -lane '\'' print "$ARGV: $_" if $. == 1 and /searchStr/i '\'' {}'
Eine weitere Idee, die Dinge zu beschleunigen, besteht darin, verfügbare Kerne und Threads zu nutzen: Das ist es, wasGNU parallelist für. Dieses Beispiel enthältperl
, macht aber dasselbe wieawk
oben3.
. Hier ist eine Befehlsaufschlüsselung:find
Suchen Sie im aktuellen Verzeichnis und seinen Unterverzeichnissen nach Dateien. Sie können ein anderes Verzeichnis angeben, in dem gesucht werden soll, und ein Dateimuster oder eine Erweiterung, nach der gefiltert werden soll:find /cygdrive/c/Directory/To/Search -iname "*.txt"
.|
„Pipe“, d. h. die Ergebnisliste an den nächsten Befehl weiterleiten.parallel
Führen Sie den nächsten Befehl parallel aus.perl
Skriptsprache, die sich hervorragend zur Textdateibearbeitung eignet und ersetzen kannsed
oderawk
.-lane
nützlicher Satz von Schaltern für Perl-Einzeiler.'\''
Escape-Apostroph, erforderlich, da wir nach bereits einen Apostrophsatz geöffnet habenparallel
.print "$ARGV: $_"
Drucken Sie den Dateinamen ($ARGV
), einen Doppelpunkt, ein Leerzeichen und die vollständige Zeile ($_
).if
Führen Sie die vorherige Anweisung nur aus, wenn die folgende(n) Bedingung(en) erfüllt sind.$. == 1
Zeilennummer ($.
) ist gleich eins (1
), d. h. wir betrachten die erste Zeile der Datei.and
Außerdem muss folgende Bedingung erfüllt sein./searchStr/i
die untersuchte Zeile enthält den TextsearchStr
, ohne Berücksichtigung der Groß-/Kleinschreibung.'\''
Ein weiteres maskiertes Apostroph markiert das Ende derperl
Anweisung.{}
Dies wird durchparallel
den jeweils von übergebenen Dateinamen ersetztfind
.'
Ende derparallel
Anleitung.
Aktualisieren:Beides awk
und perl
lesen Sie die gesamte Datei, auch wenn Aktionen nur an die erste Zeile gebunden sind. Die Lösung besteht darin, die Ausarbeitung explizit in Zeile 2 zu beenden:
find -exec awk 'BEGIN {IGNORECASE=1} NR > 1 {exit} /searchStr/ {print FILENAME": "$0}' {} \;
find | parallel 'perl -lape '\'' exit if $. == 2; print "$ARGV: $_" if /searchStr/i '\'' {}'