
Wie kann ich mit einer Shell wie Bash oder Zshell eine rekursive Such- und Ersetzungsfunktion ausführen? Mit anderen Worten: Ich möchte in allen Dateien in diesem Verzeichnis und seinen Unterverzeichnissen jedes Vorkommen von „foo“ durch „bar“ ersetzen.
Antwort1
Dieser Befehl erledigt dies (getestet sowohl auf Mac OS X Lion als auch auf Kubuntu Linux).
# Recursively find and replace in files
find . -type f -name "*.txt" -print0 | xargs -0 sed -i '' -e 's/foo/bar/g'
So funktioniert das:
find . -type f -name '*.txt'
findet im aktuellen Verzeichnis (.
) und darunter alle regulären Dateien (-type f
), deren Namen mit.txt
|
übergibt die Ausgabe dieses Befehls (eine Liste von Dateinamen) an den nächsten Befehlxargs
sammelt diese Dateinamen und übergibt sie einzeln ansed
sed -i '' -e 's/foo/bar/g'
bedeutet „bearbeiten Sie die Datei an Ort und Stelle, ohne eine Sicherungskopie zu erstellen, und nehmen Sie die folgende Ersetzung (s/foo/bar
) mehrmals pro Zeile vor (/g
)“ (sieheman sed
)
Beachten Sie, dass der Teil „ohne Sicherung“ in Zeile 4 für mich in Ordnung ist, da die Dateien, die ich ändere, ohnehin der Versionskontrolle unterliegen und ich sie im Falle eines Fehlers problemlos rückgängig machen kann.
Damit ich mir das nicht merken muss, verwende ich ein interaktives Bash-Skript wie folgt:
#!/bin/bash
# find_and_replace.sh
echo "Find and replace in current directory!"
echo "File pattern to look for? (eg '*.txt')"
read filepattern
echo "Existing string?"
read existing
echo "Replacement string?"
read replacement
echo "Replacing all occurences of $existing with $replacement in files matching $filepattern"
find . -type f -name $filepattern -print0 | xargs -0 sed -i '' -e "s/$existing/$replacement/g"
Antwort2
find . -type f -name "*.txt" -exec sed -i'' -e 's/foo/bar/g' {} +
Dadurch wird die xargs
Abhängigkeit entfernt.
Antwort3
Wenn Sie Git verwenden, können Sie Folgendes tun:
git grep -lz foo | xargs -0 sed -i '' -e 's/foo/bar/g'
-l
listet nur Dateinamen auf. -z
druckt nach jedem Ergebnis ein Null-Byte.
Ich habe das letztendlich gemacht, weil einige Dateien in einem Projekt keinen Zeilenumbruch am Ende der Datei hatten und sed einen Zeilenumbruch hinzugefügt hat, obwohl es sonst keine Änderungen vorgenommen hat. (Kein Kommentar dazu, ob Dateien am Ende einen Zeilenumbruch haben sollten oder nicht.
Antwort4
Hier ist meine zsh/Perl-Funktion, die ich dafür verwende:
change () {
from=$1
shift
to=$1
shift
for file in $*
do
perl -i.bak -p -e "s{$from}{$to}g;" $file
echo "Changing $from to $to in $file"
done
}
Und ich würde es ausführen mit
$ change foo bar **/*.java
(Zum Beispiel)