Ripgrep: Ein Suchwerkzeug, das schneller ist als grep, ag, Git grep und andere (2016)
(blog.burntsushi.net)- ripgrep (
rg) ist ein in Rust geschriebenes Kommandozeilen-Suchwerkzeug, das den Komfort der Code-Suche im Stil von The Silver Searcher mit der Rohleistung auf dem Niveau von GNU grep verbindet und Binärdateien für Linux, Mac und Windows bereitstellt. - In 25 Benchmarks gab es kein Werkzeug, das
ripgrepsowohl bei einzelnen großen Dateien als auch bei der Suche in großen Verzeichnissen in Performance und Genauigkeit klar überlegen war; auch die Kosten für Unicode-Unterstützung blieben gering. - Mit
.gitignore-Verarbeitung, standardmäßigem Ausschluss versteckter und binärer Dateien, Dateityp-Filtern, optionaler PCRE2-Unterstützung, Suche in mehreren Encodings und komprimierten Dateien sowie Preprocessing-Filtern erweitert es den praktischen Einsatzbereich von Code-Suchwerkzeugen. - Die Unterschiede zwischen den Experimenten mit dem Linux-Kernel-Repository und OpenSubtitles2016 hängen stark von Literal-Optimierungen, der SIMD-Mehrmustersuche Teddy, Aho-Corasick, der Art der UTF-8-Decodierung, dem Zeilenzählen und den Kosten der
.gitignore-Verarbeitung ab. - Beim parallelen Durchsuchen vieler kleiner Dateien können Memory Maps langsamer sein, während sie bei einer einzelnen großen Datei vorteilhaft sein können;
ripgrepnutzt daher je nach Situation entweder die Suche über Zwischenpuffer oder über Memory Maps.
Die Position, auf die ripgrep abzielte
ripgrepist ein Kommandozeilen-Suchwerkzeug, das sowohl den Komfort von Code-Suchwerkzeugen als auch die Performance vongrep-artigen Werkzeugen anstrebt.- Verglichen werden
GNU grep,git grep,The Silver Searcher (ag),Universal Code Grep (ucg),The Platinum Searcher (pt)undsift. - Die Benchmarks sollten im Kern drei Punkte überprüfen:
- Es gibt kein Werkzeug, das
ripgrepsowohl bei der Suche in einzelnen Dateien als auch in großen Verzeichnissen klar überlegen ist. - Es bietet vollständige Unicode-Unterstützung, ohne dafür hohe Performance-Kosten zu verlangen.
- Beim gleichzeitigen Durchsuchen vieler Dateien können Memory Maps im Allgemeinen eher langsamer als schneller sein.
- Es gibt kein Werkzeug, das
- Der Autor ist der Entwickler von
ripgrepund der zugrunde liegenden Regex-Engine und weist darauf hin, dass die Benchmarks ausgewählt und damit voreingenommen sein können.
Funktionen und Standardverhalten
- Der Name der ausführbaren Datei von
ripgreplautetrg. - Die Standardsuche durchsucht das aktuelle Verzeichnis rekursiv, respektiert
.gitignoreund überspringt versteckte sowie binäre Dateien. - Auch
.rgignorewird unterstützt; Muster in.rgignorehaben Vorrang vor.gitignore. - Mit
-u,-uuund-uuulässt sich der Umfang erweitern: Ignore-Dateien ignorieren, versteckte Dateien einbeziehen und binäre Dateien einbeziehen.rg -uuuähneltgrep -a -r.
- Dateityp-Filter werden unterstützt.
rg -tpy foo: nur Python-Dateien durchsuchenrg -Tjs foo: JavaScript-Dateien ausschließen- Mit
--type-addkönnen neue Dateityp-Regeln hinzugefügt werden.
- Außerdem bietet es viele Funktionen von
grep.- Kontextausgabe
- Suche nach mehreren Mustern
- farbliche Hervorhebung
- vollständige Unicode-Unterstützung
- Die Standard-Regex-Engine unterstützt kein Look-around und keine Backreferences; wählt man mit
-Pjedoch die PCRE2-Engine, stehen diese Funktionen zur Verfügung. - Unterstützt werden auch eine teilweise automatische Erkennung von UTF-16 sowie die Angabe des Encodings über
-E/--encoding.- Dazu gehören UTF-16, latin-1, GBK, EUC-JP, Shift_JIS usw.
- Mit
-z/--search-zipwird die Suche in komprimierten Dateien wie gzip, xz, lzma, bzip2 und lz4 unterstützt. - Auch beliebige Preprocessing-Filter wie PDF-Textextraktion, zusätzliche Dekomprimierung, Entschlüsselung und automatische Encoding-Erkennung werden unterstützt.
Gründe, es nicht zu verwenden
- Wenn Portabilität und die Verfügbarkeit überall oberste Priorität haben, ist das standardkonforme und weit verbreitet installierte grep passend.
- Wenn man von einer bestimmten Funktion oder einem Bug in einem anderen Werkzeug abhängt, kann
ripgrepungeeignet sein. - In einigen Performance-Edge-Cases können andere Werkzeuge besser funktionieren.
- Wenn es nicht installiert werden kann oder die Plattform nicht unterstützt wird, lässt es sich ebenfalls nicht verwenden.
Arbeitsweise von grep-artigen Werkzeugen
- Suchwerkzeuge durchlaufen grob drei Schritte:
- zu durchsuchende Dateien sammeln
- eigentliche Suche
- Ergebnisse ausgeben
- Da
grep-artige Werkzeuge große Dateien gut durchsuchen müssen, ist die Performance der Regex-Engine wichtig. ack-artige Werkzeuge müssen rekursive Verzeichnissuche und die Anwendung von Ignore-Regeln wie.gitignoreschnell verarbeiten.ripgrepversucht, beide Ansätze zu kombinieren:- schnelle Regex-Engine
- parallele Suche
- Filterung der Suchziele
Dateisammlung und Ignore-Verarbeitung
- Bei
ack-artigen Werkzeugen ist es wichtig, schnell zu entscheiden, welche Dateien im aktuellen Verzeichnis durchsucht werden sollen. - Die Performance beim Durchlaufen von Verzeichnissen hängt von der Zahl unnötiger
stat-Aufrufe ab. ripgrepverwendet einen rekursiven Verzeichnis-Iterator, der auf ein Minimum an Systemaufrufen abzielt.- Die Verarbeitung von
.gitignoreverursacht Kosten.- In jedem Verzeichnis müssen Ignore-Dateien gesucht werden.
- Ignore-Muster müssen kompiliert werden.
- Die Muster müssen auf alle Kandidatenpfade angewendet werden.
- Im Linux-Kernel-Repository gab es 4.640 Verzeichnisse und 178
.gitignore-Dateien. ripgrepversucht, die Semantik von.gitignorevollständiger zu unterstützen, und gibt dem zuletzt definierten passenden Muster Priorität.ucgkann schneller sein, weil es statt.gitignorewhitelist-basierte Glob-Regeln verwendet, kann dadurch aber Dateien mit unbekannten Erweiterungen übersehen.
Unterschiede bei Regex-Engines
- Regex-Engines lassen sich im Allgemeinen in zwei Gruppen einteilen:
- Backtracking-basiert: funktionsreich, kann bei bestimmten Eingaben aber auf exponentielle Laufzeit zurückfallen.
- endliche Automaten-basiert: Funktionsumfang kann eingeschränkt sein, garantiert aber lineare Laufzeit bezogen auf die Länge des Suchtexts.
- Die Engines der Werkzeuge sind wie folgt:
- GNU grep,
git grep: eigene Engines auf Basis endlicher Automaten ripgrep: Rust-regex-Bibliothek, auf Basis endlicher Automatenag,ucg: PCRE-basiertes Backtrackingpt,sift: Go-regex-Bibliothek, auf Basis endlicher Automaten
- GNU grep,
agunducgkönnen durch die Nutzung von PCRE dem schlechtesten Backtracking-Verhalten ausgesetzt sein.- Das Beispielmuster
(a*)* ckann bei PCRE-basierten Werkzeugen Probleme verursachen, während die anderen in den Benchmarks betrachteten Werkzeuge es problemlos verarbeiten.
Literal-Optimierung und SIMD
- Bei der Suche nach einfachen Zeichenketten kann Literal-Suchoptimierung wichtiger werden als die Regex-Engine.
- Boyer-Moore ist ein klassischer Algorithmus für die Teilzeichenkettensuche und kann Routinen wie
memchrnutzen, um Kandidatenpositionen schnell zu finden. memchr-Implementierungen prüfen häufig mithilfe von SIMD-Instruktionen 16 Byte auf einmal und können einen Durchsatz von mehreren GB/s erreichen.- Die Rust-regex-Bibliothek extrahiert aggressiv Prefix- und Suffix-Literale aus Mustern.
foo|bar(a|b)c[ab]foo[yz](foo)?bar(foo)*bar(foo){3,6}
- Wenn der gesamte reguläre Ausdruck in ein einzelnes Literal oder eine Literal-Alternation zerlegt werden kann, muss die zentrale Regex-Engine unter Umständen gar nicht verwendet werden.
ripgrepnutzt die Eigenschaft der zeilenweisen Ergebnisausgabe und extrahiert auch innere Literale.- Beispiel: Bei
\w+foo\d+wird zuerstfoogesucht, und nur die Kandidatenzeilen werden mit der Regex verifiziert.
- Beispiel: Bei
- Für die Suche nach mehreren Literalen verwendet GNU grep einen Commentz-Walter-ähnlichen Algorithmus, während Rust regex Aho-Corasick oder den Teddy-SIMD-Algorithmus nutzt.
- Teddy ist ein SIMD-basierter Mehrmuster-Suchalgorithmus aus Intel Hyperscan und eine der zentralen Optimierungen, mit denen
ripgrepGNU grep übertrifft.
Suchmethode: zeilenweise Suche vermeiden
- Eine naive Implementierung liest eine Datei zeilenweise und wendet das Muster auf jede Zeile an, doch da Treffer bei den meisten Suchen selten sind, ist das ineffizient.
- Suchwerkzeuge durchsuchen üblicherweise große Byte-Puffer auf einmal.
- Datei per Memory Map abbilden
- gesamte Datei in den Speicher lesen
- schrittweise Suche mit einem Zwischenpuffer fester Größe
ripgrep, GNU grep undgit grepunterstützen schrittweise Suche und sind damit sowohl auf Dateien als auch auf Streams anwendbar.- Schrittweise Suche ist schwer zu implementieren.
- Berechnung von Zeilennummern
- Behandlung von Fällen, in denen ein Puffer mitten in einer Zeile endet
- Behandlung langer Zeilen
- Behandlung von invert match
- Ausgabe von Kontext um Treffer herum
ripgrepnimmt die Implementierungskomplexität in Kauf und nutzt schrittweise Suche; in Benchmarks zeigte es beim Durchsuchen vieler kleiner Dateien schnellere Ergebnisse als mit Memory Maps.
Ausgabe und Parallelität
- Wenn bei paralleler Suche jeder Thread sofort ausgibt, können sich Ergebnisse aus unterschiedlichen Dateien vermischen
- Alle parallelen Code-Suchwerkzeuge schreiben Suchergebnisse in einen Zwischenpuffer im Speicher und serialisieren nur die Ausgabephase
- Dieser Ansatz ermöglicht es den Such-Threads, die eigentliche Suche parallel auszuführen
- Der Nachteil ist, dass der Speicherverbrauch stark steigen kann, etwa bei einer 2-GB-Datei, in der jede Zeile matcht
ripgrepschreibt bei Suchen überstdinoder in einer einzelnen Datei ohne Zwischenpuffer direkt nachstdout
Benchmark-Methodik
- Die Benchmarks sind nach Problemen aus Sicht der Endnutzer unterteilt
- Suche in großen Code-Repositories
- Suche in einer einzelnen großen Datei
- Die Suchmuster sind auf einfache Literale, Alternation und leichte reguläre Ausdrücke ausgerichtet
- Da sich das Standardverhalten der Tools unterscheidet, wurde für einen fairen Vergleich versucht, Bedingungen wie Zeilennummern, Unicode,
.gitignoreund Whitelists anzugleichen - Die Benchmark-Versionen waren folgende
ripgrepv0.1.2- GNU grep v2.25
git grepv2.7.4agCommitcda635, PCRE 8.38ucgCommit487bfb, PCRE 10.21 JITptCommit509368siftCommit2d175c
ackwurde ausgeschlossen, weil es damals deutlich langsamer war als die anderen Tools- Der Benchmark-Runner ist
benchsuite, das Python 3.5 oder höher benötigt, und ist imripgrep-Repository enthalten - Jeder Befehl wurde vor der Messung dreimal als Warm-up ausgeführt, damit der Korpus im OS Page Cache liegt
- Jeder Befehl wurde zehnmal gemessen; Mittelwert und Standardabweichung wurden aufgezeichnet
- Die Laufzeitumgebung war Amazon EC2
c3.2xlarge, Ubuntu 16.04, Xeon E5-2680 2,8 GHz, 16 GB Arbeitsspeicher und 80 GB SSD - Konfigurationslogs, zusammengefasste Ergebnisse und rohe CSV-Daten wurden ebenfalls veröffentlicht
Ergebnisse der Suche im Linux-Kernel-Code
- Der Code-Suchbenchmark wurde in einem gebauten Linux-Kernel-Repository mit Commit
d0acc7ausgeführt - Der Grund für die Verwendung eines gebauten Kernel-Repositories war, dass Build-Artefakte im Repository verbleiben und Relevanz sowie Performance der Suchergebnisse beeinflussen können
- In
linux_literal_defaultzeigt die Suche nach dem einfachen LiteralPM_RESUMEdie Unterschiede im Standardverhalten der einzelnen Toolsrgrespektiert.gitignoreund überspringt versteckte sowie binäre Dateienagundptverhalten sich ähnlich, zählen aber Zeilenucgliest.gitignorenicht und sucht whitelist-basiertsiftdurchsucht standardmäßig nahezu allesgit grephat den Vorteil, die Menge der zu durchsuchenden Dateien aus dem Git-Index zu beziehen
- Das Respektieren von
.gitignoreerhöht die Relevanz der Ergebnisse, kann aber Performance kosten - In
linux_literalzeigterg (whitelist)nahezu die gleiche Performance wieucg, währendrg (ignore)etwa auf dem Niveau vongit greplag rg (ignore) (mmap)undag (ignore) (mmap)wurden durch die Nutzung von Memory Mapping langsamer; unter denselben Bedingungen warrg (ignore)deutlich schneller- Auch auf einem lokalen Rechner waren die Memory-Map-Versionen langsamer, der Unterschied war jedoch geringer als auf EC2
Unicode und Suche ohne Beachtung der Groß-/Kleinschreibung
- In
linux_literal_caseiwurdeptdeutlich langsamer, weil es-ials(?i)der Go-regexp verarbeitete siftwurde weniger stark langsamer, weil es Muster und Suchblöcke in Kleinbuchstaben umwandelt; diese Optimierung behandelt jedoch nur ASCII-Groß-/Kleinschreibung und ist für Unicode-Groß-/Kleinschreibung nicht korrektripgrepwandelt case-insensitive Suche in mögliche Literal-Kombinationen um und findet Kandidatenpositionen schnell mit Teddy- Die Suche
\wAhinlinux_unicode_wordprüft, ob ein Unicode-bewusstes\wErgebnisse wieµAhfindet - Nur
rgundgit grepkonnten Unicode umschalten;ag,pt,siftunducgverwendeten ein reines ASCII-\w git grepverursachte bei aktiviertem Unicode-Support hohe Performance-Kosten, währendripgrepkaum langsamer wurderipgrepintegriert die UTF-8-Dekodierung in die endliche Zustandsmaschine und matcht direkt auf UTF-8-Byte-Strings, ohne separaten Dekodierungsschritt
Unterschiede nach Regex-Komplexität
- Bei regulären Ausdrücken mit Literal-Suffix wie
[A-Z]+_RESUMEnutzenrgunducg_RESUME, um schnell Kandidaten zu finden - Bei Literal-Alternation wie
ERR_SYS|PME_TURN_OFF|LINK_REQ_RST|CFG_BME_EVTverwendetripgrepTeddy und kann die zentrale Regex-Engine unter Umständen gar nicht einsetzen - Auch bei case-insensitiver Alternation erzeugt
ripgrepPräfixe aus Groß-/Kleinschreibungs-Kombinationen, findet Kandidaten mit Teddy und verifiziert nur diese Kandidaten mit der vollständigen Regex - Bei der Suche nach
\p{Greek}unterstützten nur Rust regex und Go regex diese Unicode-Eigenschaft;rgwar deutlich schneller alsptundsift - Bei case-insensitiver Suche nach
\p{Greek}meldetesiftkeine Treffer, undptbehandelte Unicode-Groß-/Kleinschreibung nicht korrekt - Bei Mustern ohne Literale wie
\w{5}\s+...zeigt sich die Performance der Regex-Engine direktrgwar auch mit aktiviertem Unicode-Support vergleichsweise schnellgit grepverursachte mit Unicode-Support hohe Kosten- Ein Unicode-DFA verarbeitet eine deutlich größere Menge von NFA-Zuständen als ein ASCII-DFA; die Beispielzahlen liegen bei etwa 250 NFA-Zuständen für ASCII und etwa 77.000 für Unicode
Suche in einer einzelnen großen Datei
- Der Benchmark für einzelne Dateien verwendete Samples aus OpenSubtitles2016
- Das englische Sample war etwa 1 GB groß
- Das russische Sample war etwa 1,6 GB groß
- In diesem Bereich werden die Performance der Regex-Engine und Literal-Optimierungen wichtiger
- In
subtitles_literalwarrgsowohl bei der Suche nachSherlock Holmesals auch nachШерлок Холмсam schnellsten ripgrepversucht, bei Literalen seltene Bytes auszuwählen und sie fürmemchrzu verwenden- Eine Standardimplementierung von Boyer-Moore verwendet für die Kandidatensuche normalerweise das letzte Byte
rgversucht, ein selteneres Byte auszuwählen, um in der SIMD-optimierten Schleife länger überspringen zu können
- Bei russischen Mustern beginnen in UTF-8 viele Zeichen mit
\xD0oder\xD1, sodass die Suche nach dem ersten Byte ineffizient sein kann rgnutzt eine vorberechnete 256-Byte-Häufigkeitstabelle und bevorzugt seltenere Bytes statt\xD0oder\xD1- Bei einer einzelnen großen Datei muss die Memory Map nur einmal erstellt werden; daher war die Memory-Map-Suche von
rgetwa 25 % schneller alsrg (no mmap)
Unicode und Alternation in einzelnen Dateien
- In
subtitles_literal_caseiverarbeitetrgUnicode-Suche ohne Beachtung der Groß-/Kleinschreibung korrekt und bleibt zugleich schnell - GNU grep verursacht bei Unicode-Suche ohne Beachtung der Groß-/Kleinschreibung hohe Kosten
- Bei der russischen case-insensitiven Suche scheint
grep (ASCII)-ifaktisch zu ignorieren, undagmeldet 0 Treffer - In
subtitles_alternatewarrgbei der Alternation-Suche nach mehreren Personennamen sowohl auf Englisch als auch auf Russisch am schnellsten - Bei englischer Alternation war
rgetwa um eine Größenordnung schneller als GNU grep - In
subtitles_alternate_caseiwurdergdeutlich langsamer als zuvor, lag im Englischen aber weiterhin vor den anderen Tools - In diesem Fall wurden die Literal-Kandidaten zu zahlreich für Teddy, sodass
rgstatt Teddy auf Aho-Corasick umschaltet ripgrepverwendet das transition-table-basierte „advanced“ Aho-Corasick und führt pro Eingabebyte eine Transition aus
Inner Literals und Muster ohne Literale
- Muster wie
\w+\s+Holmes\s+\w+wurden so konstruiert, dass sie Prefix- und Suffix-Literal-Optimierungen umgehen, können aber das interne LiteralHolmesnutzen ripgrepund GNU grep führen eine Inner-Literal-Optimierung durchripgrepnutztregex-syntaxder Rust-Regex-Engine, um Literale aus dem Pattern-AST zu extrahieren- Bei der russischen Version
\w+\s+Холмс\s+\w+konnten nur Tools mit korrekter Unicode-Unterstützung sinnvolle Ergebnisse liefern - Bei einem langen Muster ganz ohne Literale wie
\w{5}\s+...gehörtergim Englischen zu den schnellsten; die Version von GNU grep mit Unicode-Unterstützung brauchte im Englischen über 90 Sekunden und im Russischen über 4 Minuten und wurde daher ausgeschlossen ripgreperhält Unicode-Unterstützung und Performance, indem es die UTF-8-Dekodierung in den DFA integriert
Zusätzliche Benchmarks
everythingist ein unrealistischer Test, der im Linux-Repository mit.*alle Zeilen matchtrgmeldete 22.065.361 Zeilen in 1,081 Sekundenagundptmeldeten nicht alle Zeilen, daher scheint es ein Match-Limit zu geben
nothingist ein Test, der auf.*invert match anwendet und dadurch keine Zeilen meldetrgkam auf 0,302 Sekunden,git grepauf 0,905 Sekundenptunducgunterstützen invert search nicht
contextgibt im englischen Untertitelkorpus 2 Zeilen Kontext rund umSherlock Holmesausrglag bei 0,612 Sekunden,siftmit 0,717 Sekunden ähnlichucgunterstützt diese Funktion nicht
hugedurchsucht die gesamten 9,3 GB englischer Untertitel nachSherlock Holmesrglag bei 1,786 Sekunden, GNU grep bei 5,119 Sekunden,siftbei 3,047 Sekundenucgmeldete unter der Line-Counting-Bedingung nur 1.543 Zeilen und lieferte damit ein falsches Ergebnis; vermutet wurde ein Problem bei der Suche in Dateien über 2 GB
Fazit
ripgrepgewann bei der Suche im Linux-Kernel-Repository nicht immer jeden Benchmark, doch bei Performance und Korrektheit ließ sich schwer sagen, dass andere Tools klar überlegen warengit grepkonnte in einigen einfachen Fällen um wenige Millisekunden vorn liegen, doch wenn Muster komplexer wurden oder Unicode erforderlich war, lagripgrepteils deutlich vorn- Zur Code-Suchleistung von
ripgreptragen folgende Faktoren bei- Schnelle Verzeichnisdurchläufe mit dem Ziel,
stat-Aufrufe zu minimieren .gitignore-Glob-Matching mitRegexSet- Arbeitsverteilung über eine Chase-Lev-Work-Stealing-Queue
- Die Entscheidung, bei der Suche in vielen kleinen Dateien keine Memory Maps zu verwenden
- Eine schnelle Regex-Engine
- Schnelle Verzeichnisdurchläufe mit dem Ziel,
- Bei der Suche in einzelnen Dateien war
ripgrepin allen wichtigen Benchmarks entweder am schnellsten oder mit großem Abstand vorn - Die Performance bei einzelnen Dateien wird durch sparsity-byte-basiertes
memchr, Teddy SIMD, Aho-Corasick und einen DFA mit integrierter UTF-8-Dekodierung beeinflusst - In Benchmarks, die Unicode-Funktionalität erforderten, zeigten nur
rg, GNU grep undgit grepsinnvolle Unterstützung; GNU grep undgit grepzahlten dafür meist einen hohen Performance-Preis - Memory Maps waren unter Linux x86_64 bei der parallelen Suche in vielen kleinen Dateien nachteilig, bei der Suche in einer einzelnen großen Datei vorteilhaft und können in VM-Umgebungen zusätzliche Einbußen verursachen
1 Kommentare
Hacker-News-Kommentare
Definitiv schnell, und ich empfehle weiterhin die Kombination mit fzf
Ich nutze eine PowerShell-Funktion, die zuerst mit
ripgrepsucht, dann eine Fuzzy-Suche über Ergebnisdateien und Text legt und mitbatden Kontext anzeigt.In Projekten, in denen mehrere Repositories vermischt sind, kann man damit sehr schnell eingrenzen, wenn man „weiß, dass es irgendwo ist, aber weder den genauen Ort noch den Namen kennt“.
Dieser Ansatz stammt aus https://github.com/junegunn/fzf/blob/master/ADVANCED.md, und auch wenn man nicht alles davon nutzt, lohnt sich ein Überfliegen, um Ideen mitzunehmen.
fzfzu integrieren.Damit kann man nicht nur Textdateien, sondern auch viele andere Dateiformate wie PDF oder ZIP per Fuzzy-Suche durchsuchen.
Details stehen hier: https://github.com/phiresky/ripgrep-all/wiki/fzf-Integration
Dabei wählt man die
rg-Ergebnisse mitfzfaus, parst die ausgewählte Datei und Zeilennummer und öffnet dann mit$EDITOR +"${linenumber}" "$file".Wie Kaffee von Hand zu mahlen statt mit einer elektrischen Mühle.
fzfkann man aus vielen Dateien auswählen, die zu Git hinzugefügt werden sollen, und einzelne überspringen.Wenn man in der
gitconfigunter[alias]fza = "!git ls-files -m -o --exclude-standard | fzf -m --print0 | xargs -0 git add"einträgt, zeigtgit fzaeine Liste geänderter oder noch nicht hinzugefügter Dateien an, und man kann mit der Leertaste Einträge umschalten und zum nächsten gehen.Dieser Alias und fzf+fd beschleunigen Teile des Workflows ziemlich deutlich.
Es gibt auch einen Leitfaden dazu, was man unter macOS in die zsh-Konfiguration aufnehmen kann: https://gist.github.com/aclarknexient/0ffcb98aa262c585c49d4b...
ripgrepfast auf die gleiche Weise.In einer Codebasis mit Hunderten von Repositories ist es mein Ausgangspunkt, um Dateien oder Projekte einzugrenzen, bevor ich tiefer einsteige.
In Emacs verwende ich
ripgrepmit project.el und dem Paket dumb-jump.Vielleicht ist das nicht die populärste Methode, aber das Gesamterlebnis ist ziemlich zufriedenstellend.
Man muss nur
dumb-jumpperpackage-installinstallieren und(add-hook 'xref-backend-functions #'dumb-jump-xref-activate)setzen.Wenn man in Python-Projekten mit
M-.oderC-u M-.die Definition eines Bezeichners sucht, führtdumb-jumppassend zum aktuellen Projekt und Dateityp einenrg-Befehl aus und zeigt die Ergebnisse im Xref-Puffer an.agwird ebenfalls unterstützt, und wenn wederagnochrgvorhanden ist, fällt es aufgrepzurück, was erwartungsgemäß langsam sein kann, wenn das gesamte Home-Verzeichnis durchsucht wird.ripgrepziemlich einfach nutzen.Externe Pakete sind nicht zwingend nötig; wenn man in großen Verzeichnissen statt des langsamen
greplieberripgrepverwenden möchte, setzt man einfach(setq xref-search-program 'ripgrep).Dann wird eine Projektsuche wie
C-x p g foo RETim aktuellen Projekt in der Formrg -i --null -nH --no-heading --no-messages -g '!*/' -e fooausgeführt.Die Ergebnisse erscheinen im Xref-Puffer, sodass man mit Tasten wie
n,p,RETundC-obequem zum nächsten oder vorherigen Treffer springen, zur Quelle wechseln oder sie in einem geteilten Fenster anzeigen kann.ripgrep-Autors würde ich sagen: Ich habe diesen regulären Ausdruck nicht direkt ausprobiert, aber die --pcre2-Option könnte vermutlich weggelassen werden.Die zweite und dritte
\b-Assertion könnten wohl auch entfallen, die erste könnte jedoch nötig sein.ripgrepund hat auch Bindings fürevil-collection, damit lässt es sich gut verwenden: https://github.com/Wilfred/deadgrepDas sind die Fälle, in denen ich früher
rgrepverwendet hätte.Interessant ist, dass auch die VS-Code-Suche inzwischen über einen Node.js-Wrapper mit
ripgreparbeitet.https://www.npmjs.com/package/@vscode/ripgrep
ripgrepnicht installieren darf.Im VS-Installationspfad kann man die
rg-Binärdatei finden. Zumindest in meiner Windows-Arbeitsumgebung war das möglich.Ich benutze
ripgrepseit etwa zwei Jahren, und es ist inzwischen ein unverzichtbares Werkzeug.Der Hauptgrund für den Umstieg von
grepwar die Benutzerfreundlichkeit.Standardmäßig beachtet es
.gitignore-Regeln und überspringt versteckte Dateien/Verzeichnisse sowie Binärdateien, daher istrg suchbegriff verzeichnisdem entsprechendengrep-Befehl weit überlegen; der Geschwindigkeitsgewinn ist ein zusätzlicher Bonus.Wenn Treffer zu lang sind und das Terminal dadurch unübersichtlich wird, nutze ich oft die Option
-M, etwa als-M 1000.-Mist wirklich großartig.Besonders praktisch ist es, um Ergebnisse aus minifizierten Dateien zu ignorieren, die man nicht sehen will, und auch die Option
-gist nützlich, um nur Dateien mit einer bestimmten Erweiterung zu durchsuchen, etwa mit-g *.cs.Hilfreich ist außerdem, dass es sich um eine eigenständige, portable Binärdatei handelt: Wenn ich auf einem neuen Rechner arbeite, lege ich die ausführbare Datei ab und setze den
grep-Alias aufrg, sodass beim gewohnten Tippen vongreptrotzdemrgausgeführt wird.Das mag auch 2023 noch stimmen, aber das Problem ist, dass parallelisierte
grep-Alternativen wieripgrepoderagim Vergleich zu klassischemgrepso schnell sind, dass kleine Geschwindigkeitsunterschiede untereinander kaum noch als Unterscheidungsmerkmal taugen.Ich nutze
agin Emacs auf einer Codebasis mit 900.000 Zeilen, und auf einem 16-Core Ryzen Threadripper 2950X ist es praktisch sofort fertig.Ich habe nicht das Bedürfnis, „unter 1 Sekunde“ auf „noch ein bisschen mehr unter 1 Sekunde“ zu verkürzen.
Die entscheidende Eigenschaft neuer
grep-artigen Tools ist nicht die Geschwindigkeit; man sollte sie nach anderen Kriterien bewerten und vergleichen.aghat einen ziemlich starken Performance-Einbruch, und das sieht man auch im Blogpost.Die Workloads unterscheiden sich aber von Person zu Person, deshalb ist der Performance-Unterschied in manchen Fällen möglicherweise nicht wichtig.
900.000 Zeilen sind nicht besonders viel, und bei einfachen Anfragen sind die meisten nicht-naiven
grep-artigen Tools sowieso sehr schnell.Nach anderen Vergleichskriterien ist
agfast nur noch auf lebenserhaltenden Maßnahmen, und es wäre in Debian beinahe entfernt worden, wurde aber offenbar von jemandem gerettet: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=999962Der Blogpost vergleicht auch Unicode-Unterstützung, und
aghat praktisch keine Unicode-Unterstützung. Das ist nicht für alle wichtig, reicht aber als nicht-performanzbezogenes Vergleichskriterium völlig aus.Die Suchzeit entspricht ungefähr der Zeit, die das Laden der Dateien von der Festplatte dauert, und Unterschiede danach sind kaum noch bedeutsam.
Wenn die Dateien im Cache sind, dominiert eher die Zeit, durchs Dateisystem zu navigieren und den Befehl zu tippen, statt der Suchzeit selbst, sodass Performance-Unterschiede auch dann kaum relevant sind.
(2016) gehört in den Titel.
Das ist ursprünglich ein Showcase-Post und keine neue Information.
„Ripgrep – A new command line search tool“ https://news.ycombinator.com/item?id=12564442 (740 points | Sept 23, 2016 | 209 comments) — dort gibt es auch eine Diskussion über Geschwindigkeit
„Ripgrep is faster (2016)“ https://news.ycombinator.com/item?id=17941319 (98 points | Sept 8, 2018 | 40 comments)
Schneller als
qgrepist es nicht.Beide funktionieren grundsätzlich sehr unterschiedlich;
qgrepbasiert aufre2, aber seine Geschwindigkeit kommt durch den Index.Bei großen Dateirepositories ist es sinnvoller,
qgrepmit Index zu verwenden, statt jedes Mal alle Dateien zu durchsuchen, und ich frage mich, warum Leute dieseqgrep-Option vergessen.Allerdings halte ich
ripgrepnicht für besonders schnell, wenn man mehrzeilige Treffer in UTF-8 braucht, weil es dann auf eine andere PCRE2-Bibliothek zurückgreifen muss.ripgrepkann ich bestätigen, dassqgrepdurch Indexierung einen Vorteil gegenüber nicht indexierenden Tools hat.Dafür muss man den Index aber einrichten und pflegen, weshalb die UX nicht so einfach ist wie „einfach die Suche ausführen“.
Warum Leute
qgrepnicht nutzen, ähnelt dem Grund, warum sieripgrepnicht nutzen: „Für mich ist grep schon schnell genug.“Bei kleinen Suchräumen spüren viele den Geschwindigkeitsunterschied zwischen
ripgrepundgrepoder zwischenqgrepundripgrepgar nicht.Wenn
ripgrepeine Linux-Kernel-Suche in unter 100 ms abschließt, ist der Wechsel zu einem indexierenden Tool für den üblichen interaktiven Einsatz je nach Situation vielleicht den Zusatzaufwand nicht wert — meistens aber wohl nicht.Ich habe schon einmal über die Idee nachgedacht,
ripgrepum Indexierung zu erweitern: https://github.com/BurntSushi/ripgrep/issues/1497Und für mehrzeilige Suche ist PCRE2 nicht erforderlich. Die Standard-RegEx-Engine unterstützt ebenfalls Unicode, und die Unterstützung für mehrzeilige Suche bleibt auch erhalten, wenn ohne PCRE2 gebaut wird.
Ich bin von
ripgrepzu ugrep gewechselt und habe nicht zurückgeblickt.Es ist ähnlich schnell, hat aber Fuzzy Matching, eine TUI, die sich für Code-Reviews eignet, und kann auch in PDFs oder komprimierten Dateien suchen.
Praktisch ist auch, dass man optional die Google-Suchsyntax verwenden kann.
https://ugrep.com
ripgrep-Fan, bin aber vor Kurzem wegen einer Funktion zuugrepgekommen, dieripgrepnicht hat: Suche innerhalb von ZIP-Archiven.Man kann suchen, ohne die Dateien auf die Festplatte zu entpacken.
Ich arbeite mit komprimierten Korpora aus Millionen kleiner Textdateien, und es ist großartig, nicht alles erst ins Dateisystem entpacken zu müssen. Manche Dateisysteme haben bei dieser Größenordnung Schwierigkeiten.
Ich bin für beide Tools dankbar und danke ihren jeweiligen Autoren.
grepzu benutzen beginnt, die meisten Ergebnisse versuchen werden, mir etwas zu verkaufen.ugrepundripgrepauf Reddit über Jahre hinweg gestritten zu haben scheinen.Zum Beispiel https://www.reddit.com/r/programming/comments/120wqvr/ripgre...
Es geht doch nur um Open-Source-Tools, deshalb wirkt das etwas seltsam.
fzfweiterzureichen.Für mich scheint es schwer zu sein, die Konfigurierbarkeit und Flexibilität von
fzfzu übertreffen.Das Killer-Feature scheint mir die bestehende Kompatibilität mit den
grep-Kommandozeilenoptionen zu sein.Es ist ziemlich gut, nicht einen komplett neuen Satz an Optionen lernen zu müssen.
Ich frage mich, warum
grepnicht ersetzt oder verbessert wirdDieses Thema wirkt inzwischen auch schon etwas alt
Trägheit, Kompatibilität, Widerstand gegen Veränderungen, das Innovator’s Dilemma und Ähnliches. Das ist nicht negativ gemeint; auf mich trifft das alles ebenfalls zu
Zur Kompatibilität siehe die FAQ: https://github.com/BurntSushi/ripgrep/blob/master/FAQ.md#pos...
Er ist bequem, passt gut zur übrigen Arbeitsumgebung, und es gibt keinen besonderen Grund, ihn zu ersetzen und alles neu darauf abzustimmen
Die Analogie geht nur so weit, dass zufällig ein Stuhl wie von Razer in der Nähe steht und als Kleiderablage dient
xyzgeben, das dieses Argument akzeptiert und sich exakt so verhält.“ripgrepverwendenWenn es darum geht, den
grep-Befehl selbst durch ein anderes Utility zu ersetzen, scheint dafür im Verhältnis zum Nutzen zu viel kaputtzugehenWer ein schnelleres
grepwill, kann ein anderes Tool verwenden, und wer das bestehendegrepnutzt, kann dabei bleiben — das kommt dem Idealzustand bereits sehr nahegrepist ein Allzweckwerkzeug, um Text in allen möglichen Dateitypen zu finden, und es ist in den UNIX-Standards verankertEinige Programmierer verwenden es für die Suche in Quellcode, aber andere nutzen es für Textsuche ohne Bezug zu Quellcode oder in Skripten und erwarten, dass es niemals abstürzt
ripgrepdagegen ist ein spezialisiertes, meinungsstarkes Tool, das hauptsächlich für die Suche in Quellcode-Repositories entwickelt wurdeEs gibt nicht viel Spielraum, allgemeine Textsuche noch schneller zu machen. Mit
mmap()gibt es bei abgeschnittenen Dateien ein Absturzrisiko, mit weniger Ausdrucksstärke bei regulären Ausdrücken könnte es schneller werden, und man könnte die Unterstützung für alle Locales und Zeichensätze aufgeben und nur UTF-8/UTF-16 fest einkodieren, aber das sollte man nicht tunIch habe in Portage nachgesehen, und es scheint auch eine Version zu geben, die andere Dokumente wie PDF und doc verarbeitet
https://github.com/phiresky/ripgrep-all