Der Befehl lautet grep -rl 'old' . | xargs sed -i 's/old/new/g'. Die wichtige Hälfte ist die erste: grep -rl listet die Dateien auf, die sich ändern würden, sodass Sie sie sich ansehen können, bevor sed sie an Ort und Stelle umschreibt.
1. Welche Dateien würden sich ändern?
$ clai liste die Dateien unter proj auf, die old_name enthalten→ grep -rl 'old_name' proj/proj/src/b.pyproj/src/a.py
-r durchsucht rekursiv, -l gibt Dateinamen statt Trefferzeilen aus. Zwei Dateien – eine glaubwürdige Zahl. Stünde dort 4000, ist Ihr Muster zu weit gefasst, und Sie haben sich gerade einen Nachmittag erspart.
2. Nur in genau diesen Dateien ersetzen
$ clai ersetze old_name durch new_name in allen Dateien unter proj, die es enthalten→ grep -rl 'old_name' proj/ | xargs sed -i 's/old_name/new_name/g'$ cat proj/src/a.pynew_name = 1
Es werden nur die passenden Dateien geöffnet. sed -i stattdessen über den ganzen Baum laufen zu lassen, würde die mtime jeder Datei im Repo neu schreiben, auch die Tausenden, die sich gar nicht geändert haben.
3. Dateinamen mit Leerzeichen brechen nichts
$ clai dieselbe Ersetzung, aber mit Dateinamen mit Leerzeichen sicher umgehen→ grep -rlZ 'old_name' proj/ | xargs -0 -r sed -i 's/old_name/new_name/g'
-Z lässt grep Namen mit einem Null-Byte trennen, -0 lässt xargs genau das erwarten. Ohne dieses Paar kommt meine datei.py als zwei Argumente an, und sed bearbeitet keines davon. -r verhindert, dass xargs sed ohne Argumente aufruft, wenn nichts passte.
4. Verzeichnisse überspringen, die Sie nie anfassen wollen
$ clai ersetze es, aber überspringe node_modules, .git und vendor→ grep -rlZ --exclude-dir={.git,node_modules,vendor} 'old_name' . | xargs -0 -r sed -i 's/old_name/new_name/g'
Ohne Ausschlüsse schreiben Sie irgendwann den Quellcode einer Abhängigkeit um, und der Diff hat 40.000 Zeilen.
5. Nur ein Dateityp
$ clai ersetze es nur in Python-Dateien→ grep -rlZ --include='*.py' 'old_name' . | xargs -0 -r sed -i 's/old_name/new_name/g'
--include ist das Whitelist-Gegenstück zu --exclude. Enger ist sicherer.
6. Ein Backup behalten – und portabel bleiben
$ clai mach die Ersetzung, aber behalte ein Backup jeder Datei, auch auf dem Mac→ grep -rlZ 'old_name' proj/ | xargs -0 -r sed -i.bak 's/old_name/new_name/g'
sed -i.bak schreibt a.py.bak neben jede bearbeitete Datei. Es ist außerdem die einzige Schreibweise, die sowohl bei GNU- als auch bei BSD-sed funktioniert – macOS verlangt das Suffix, Linux akzeptiert es.
Stolperfallen
- Ihr Muster ist eine Regex, kein String.
.,*,/und$sind Sonderzeichen. Das Ersetzen vonconfig.ymltrifft auchconfigXyml; maskieren Sie es oder nehmen Sie ein Trennzeichen, das nicht im Text vorkommt:sed -i 's|old/path|new/path|g'. - Erst committen.
sed -ikennt kein Rückgängig. Ein sauberer git status vor der Ersetzung macht aus einem Fehler ein einfachesgit checkout .. grep -lstoppt beim ersten Treffer pro Datei – deshalb ist es schnell, und es beeinflusst die Ersetzung nicht, weilsedtrotzdem jede Zeile durchgeht.
Verwandte Fragen
Wie sehe ich die Ersetzung vorher an, statt sie anzuwenden? Lassen Sie -i weg und drucken Sie: grep -rlZ 'old' . | xargs -0 sed 's/old/new/g' | less. Oder grep -rn 'old' ., um jede Zeile mit Zeilennummer zu sehen.
Warum ist nichts passiert? Meist hat xargs eine leere Liste bekommen, oder Ihr Muster passt nicht auf das, was Sie denken. Führen Sie nur die grep -rl-Hälfte aus.
Geht das auch mit find? Ja: find . -type f -name '*.py' -exec sed -i 's/old/new/g' {} +. Es schreibt jede passende Datei um, egal ob das Muster darin vorkam oder nicht – deshalb ist die grep -rl-Variante vorzuziehen.
Siehe auch
- Hunderte Dateien umbenennen: von
for-Schleifen zu normaler Sprache sed -ifunktioniert nicht auf dem Mac? BSD- vs. GNU-Flags, behoben- Vom Einzeiler zum eingecheckten Skript: wann der Wechsel sich lohnt
Beschreiben Sie die Ersetzung, lesen Sie den Befehl, den CliAI schreibt, und führen Sie ihn aus. In einer Zeile installieren.