it-swarm.com.de

Wie mache ich ein rekursives Suchen / Ersetzen eines Strings mit awk oder sed?

Wie finde und ersetze ich jedes Vorkommen von:

subdomainA.example.com

mit

subdomainB.example.com

in jeder Textdatei unter dem Verzeichnisbaum /home/www/ rekursiv?

618
Tedd
find /home/www \( -type d -name .git -Prune \) -o -type f -print0 | xargs -0 sed -i 's/subdomainA\.example\.com/subdomainB.example.com/g'

Aus man find:

- print (nur GNU find) weist find an, das Nullzeichen (\0) anstelle des Leerzeichens als Ausgabetrennzeichen zwischen den gefundenen Pfadnamen zu verwenden. Dies ist eine sicherere Option, wenn Ihre Dateien Leerzeichen oder andere Sonderzeichen enthalten können. Es wird empfohlen, das Argument -print0 für find zu verwenden, wenn Sie -exec <command> oder xargs verwenden (das Argument -0 wird in xargs benötigt).

795

Hinweis: Führen Sie diesen Befehl nicht in einem Ordner mit einem Git-Repo aus - Änderungen an .git können Ihren Git-Index beschädigen.

find /home/www/ -type f -exec \
    sed -i 's/subdomainA\.example\.com/subdomainB.example.com/g' {} +

Im Vergleich zu anderen Antworten ist dies einfacher als die meisten anderen und verwendet sed anstelle von Perl, was in der ursprünglichen Frage gefordert wurde.

234
John Zwinck

Der einfachste Weg für mich ist

grep -rl oldtext . | xargs sed -i 's/oldtext/newtext/g'
159
Anatoly

Alle Tricks sind fast gleich, aber ich mag diesen:

find <mydir> -type f -exec sed -i 's/<string1>/<string2>/g' {} +
  • find <mydir>: Im Verzeichnis nachschlagen.

  • -type f:

    Die Datei ist vom Typ: reguläre Datei

  • -exec command {} +:

    Diese Variante der Aktion -exec führt den angegebenen Befehl für die ausgewählten Dateien aus. Die Befehlszeile wird jedoch erstellt, indem jeder ausgewählte Dateiname am Ende angehängt wird. Die Gesamtzahl der Aufrufe des Befehls ist viel geringer als die Anzahl der übereinstimmenden Dateien. Die Befehlszeile ist in etwa so aufgebaut wie die Befehlszeilen von xargs. Innerhalb des Befehls ist nur eine Instanz von {} zulässig. Der Befehl wird im Startverzeichnis ausgeführt.

57
I159
cd /home/www && find . -type f -print0 |
  xargs -0 Perl -i.bak -pe 's/subdomainA\.example\.com/subdomainB.example.com/g'
39

Für mich ist die am einfachsten zu merkende Lösung https://stackoverflow.com/a/2113224/565525 , d. H .:

sed -i '' -e 's/subdomainA/subdomainB/g' $(find /home/www/ -type f)

NOTE: -i '' löst das OSX-Problem sed: 1: "...": invalid command code .

NOTE: Wenn zu viele Dateien zum Verarbeiten vorhanden sind, erhalten Sie Argument list too long. Problemumgehung: Verwenden Sie die oben beschriebene Lösung find -exec oder xargs.

29
Robert Lujo

Für alle, die Silver Searcher (ag) verwenden

ag SearchString -l0 | xargs -0 sed -i 's/SearchString/Replacement/g'

Da ag standardmäßig git/hg/svn-Dateien/-Ordner ignoriert, kann dies sicher in einem Repository ausgeführt werden.

25
Jacob Wang

Um Dateien so zu reduzieren, dass sie rekursiv sed durchlaufen, können Sie grep für Ihre String-Instanz:

grep -rl <oldstring> /path/to/folder | xargs sed -i s^<oldstring>^<newstring>^g

Wenn Sie man grep ausführen, werden Sie feststellen, dass Sie auch ein --exlude-dir="*.git" -Flag definieren können, wenn Sie das Durchsuchen von .git-Verzeichnissen unterlassen möchten.

Sie führen zu:

grep -rl --exclude-dir="*.git" <oldstring> /path/to/folder | xargs sed -i s^<oldstring>^<newstring>^g
15
domdambrogia

Ein One Nice Oneliner als Extra. Mit Git Grep.

git grep -lz 'subdomainA.example.com' | xargs -0 Perl -i'' -pE "s/subdomainA.example.com/subdomainB.example.com/g"
14
Jimmy Kane

Dieser ist mit Git-Repositories kompatibel und ein bisschen einfacher:

Linux:

git grep -l 'original_text' | xargs sed -i 's/original_text/new_text/g'

Mac:

git grep -l 'original_text' | xargs sed -i '' -e 's/original_text/new_text/g'

(Danke an http://blog.jasonmeridth.com/posts/use-git-grep-to-replace-strings-in-files-in-your-git-repository/ )

11
seddonym
find /home/www/ -type f -exec Perl -i.bak -pe 's/subdomainA\.example\.com/subdomainB.example.com/g' {} +

find /home/www/ -type f listet alle Dateien in/home/www/(und seinen Unterverzeichnissen) auf. Das Flag "-exec" weist find an, den folgenden Befehl für jede gefundene Datei auszuführen.

Perl -i.bak -pe 's/subdomainA\.example\.com/subdomainB.example.com/g' {} +

wird der Befehl für die Dateien ausgeführt (viele gleichzeitig). Der {} wird durch Dateinamen ersetzt. Der + am Ende des Befehls weist find an, einen Befehl für viele Dateinamen zu erstellen.

In der Manpage find heißt es: "Die Befehlszeile ist in etwa so aufgebaut wie die Befehlszeilen von xargs."

Auf diese Weise ist es möglich, Ihr Ziel zu erreichen (und mit Dateinamen mit Leerzeichen umzugehen), ohne xargs -0 oder -print0 zu verwenden.

8
unutbu

Ich brauchte das nur und war mit der Geschwindigkeit der verfügbaren Beispiele nicht zufrieden. Also habe ich mir ein eigenes ausgedacht:

cd /var/www && ack-grep -l --print0 subdomainA.example.com | xargs -0 Perl -i.bak -pe 's/subdomainA\.example\.com/subdomainB.example.com/g'

Ack-grep ist sehr effizient bei der Suche nach relevanten Dateien. Dieser Befehl ersetzte ~ 145 000 Dateien mit einem Kinderspiel, während andere so lange brauchten, dass ich nicht warten konnte, bis sie fertig waren.

7
Henno

Sie können awk verwenden, um dies wie folgt zu lösen,

for file in `find /home/www -type f`
do
   awk '{gsub(/subdomainA.example.com/,"subdomainB.example.com"); print $0;}' $file > ./tempFile && mv ./tempFile $file;
done

hoffe das wird dir helfen !!!

4
sarath kumar

grep -lr 'subdomainA.example.com' | while read file; do sed -i "s/subdomainA.example.com/subdomainB.example.com/g" "$file"; done

Ich denke, die meisten Leute wissen nicht, dass sie etwas in eine "while read file" -Pipeline leiten können, und es vermeidet diese fiesen -print0-Argumente, während Leerzeichen in Dateinamen beibehalten werden.

Wenn Sie vor dem sed ein echo hinzufügen, können Sie sehen, welche Dateien geändert werden, bevor Sie dies tatsächlich tun.

4
MadMan2064

Versuche dies:

sed -i 's/subdomainA/subdomainB/g' `grep -ril 'subdomainA' *`
3
RikHic
#!/usr/local/bin/bash -x

find * /home/www -type f | while read files
do

sedtest=$(sed -n '/^/,/$/p' "${files}" | sed -n '/subdomainA/p')

    if [ "${sedtest}" ]
    then
    sed s'/subdomainA/subdomainB/'g "${files}" > "${files}".tmp
    mv "${files}".tmp "${files}"
    fi

done
3
petrus4

Ein bisschen altmodisch, aber dies funktionierte unter OS X.

Es gibt einige Tricks:

• Es werden nur Dateien mit der Erweiterung .sls im aktuellen Verzeichnis bearbeitet

. muss maskiert werden, um sicherzustellen, dass sed sie nicht als "beliebige Zeichen" bewertet.

, wird anstelle des üblichen / als Begrenzer sed verwendet

Beachten Sie auch, dass Sie eine Jinja-Vorlage bearbeiten müssen, um variable im Pfad eines import zu übergeben (dies ist jedoch kein Thema).

Vergewissern Sie sich zuerst, dass Ihr sed-Befehl das tut, was Sie wollen (dies druckt nur die Änderungen an stdout aus, die Dateien werden nicht geändert):

for file in $(find . -name *.sls -type f); do echo -e "\n$file: "; sed 's,foo\.bar,foo/bar/\"+baz+\"/,g' $file; done

Bearbeiten Sie den sed-Befehl nach Bedarf, sobald Sie Änderungen vornehmen möchten:

for file in $(find . -name *.sls -type f); do echo -e "\n$file: "; sed -i '' 's,foo\.bar,foo/bar/\"+baz+\"/,g' $file; done

Beachten Sie den -i '' im Befehl sed , ich wollte keine Sicherungskopie der Originaldateien erstellen (wie in In-Place-Änderungen mit sed unter OS X oder in Robert Lujos Kommentar auf dieser Seite).

Glückliche seding Völker!

2
Raphvanns

Wenn es Ihnen nichts ausmacht, vim zusammen mit grep oder find Tools zu verwenden, können Sie der Antwort des Benutzers Gert in diesem Link folgen -> Vorgehensweise a Textersetzung in einer großen Ordnerhierarchie? .

Das ist der Deal:

  • suchen Sie rekursiv nach der Zeichenfolge, die Sie in einem bestimmten Pfad ersetzen möchten, und verwenden Sie nur den vollständigen Pfad der entsprechenden Datei. (das wäre die $(grep 'string' 'pathname' -Rl).

  • (optional) Wenn Sie eine Vorab-Sicherung dieser Dateien in einem zentralen Verzeichnis erstellen möchten, können Sie dies auch verwenden: cp -iv $(grep 'string' 'pathname' -Rl) 'centralized-directory-pathname'

  • danach können Sie nach Belieben in vim nach einem ähnlichen Schema wie auf dem angegebenen Link bearbeiten/ersetzen:

    • :bufdo %s#string#replacement#gc | update
2
mzcl-mn

Hier ist eine Version, die allgemeiner sein sollte als die meisten anderen. Es ist beispielsweise nicht erforderlich, find (stattdessen du zu verwenden). Es erfordert xargs, das nur in einigen Versionen von Plan 9 (wie 9front) zu finden ist.

 du -a | awk -F' '  '{ print $2 }' | xargs sed -i -e 's/subdomainA\.example\.com/subdomainB.example.com/g'

Wenn Sie Filter wie Dateierweiterungen hinzufügen möchten, verwenden Sie grep:

 du -a | grep "\.scala$" | awk -F' '  '{ print $2 }' | xargs sed -i -e 's/subdomainA\.example\.com/subdomainB.example.com/g'
2
bbarker

Laut this Blogpost:

find . -type f | xargs Perl -pi -e 's/oldtext/newtext/g;'
2
J.Hpour

Ich benutze nur Tops:

find . -name '*.[c|cc|cp|cpp|m|mm|h]' -print0 |  xargs -0 tops -verbose  replace "verify_noerr(<b args>)" with "__Verify_noErr(<args>)" \
replace "check(<b args>)" with "__Check(<args>)" 
2
tgunr

nur um zu vermeiden, auch zu ändern

  • NearlysubdomainA.example.com
  • subdomainA.Beispiel.comp.other

aber dennoch

  • subdomainA.example.com.IsIt.good

(Vielleicht nicht gut in der Idee hinter Domain Root)

find /home/www/ -type f -exec sed -i 's/\bsubdomainA\.example\.com\b/\1subdomainB.example.com\2/g' {} \;
2
NeronLeVelu

so ändern Sie mehrere Dateien (und speichern ein Backup als *.bak):

Perl -p -i -e "s/\|/x/g" *

nimmt alle Dateien in ein Verzeichnis und ersetzt | durch x, das als "Perl-Torte" bezeichnet wird (einfach wie eine Torte).

1
Stenemo

Für Qshell (qsh) unter IBMi, nicht als OP-gekennzeichnete Bash.

Einschränkungen von qsh-Befehlen:

  • find hat die Option -print0 nicht
  • xargs hat keine Option -0
  • sed hat keine Option -i

Also die Lösung in qsh:

    PATH='your/path/here'
    SEARCH=\'subdomainA.example.com\'
    REPLACE=\'subdomainB.example.com\'

    for file in $( find ${PATH} -P -type f ); do

            TEMP_FILE=${file}.${RANDOM}.temp_file

            if [ ! -e ${TEMP_FILE} ]; then
                    touch -C 819 ${TEMP_FILE}

                    sed -e 's/'$SEARCH'/'$REPLACE'/g' \
                    < ${file} > ${TEMP_FILE}

                    mv ${TEMP_FILE} ${file}
            fi
    done

Vorsichtsmaßnahmen:

  • Lösung schließt Fehlerbehandlung aus
  • Nicht Bash wie von OP markiert
1
Perl -p -i -e 's/oldthing/new_thingy/g' `grep -ril oldthing *`
1
Sheena

Zum Ersetzen aller Vorkommen in einem Git-Repository können Sie Folgendes verwenden:

git ls-files -z | xargs -0 sed -i 's/subdomainA\.example\.com/subdomainB.example.com/g'

Weitere Optionen zum Auflisten aller Dateien in einem Repository finden Sie unter Dateien in lokalem Git-Repository auflisten? . Die -z -Optionen weisen git an, die Dateinamen mit einem Null-Byte zu trennen, wodurch sichergestellt wird, dass xargs (mit der Option -0) Dateinamen auch dann trennen kann, wenn sie Leerzeichen oder ähnliches enthalten.

1
Perseids

Eine einfache Methode, wenn Sie Verzeichnisse ausschließen (--exclude-dir=.svn) benötigen und auch Dateinamen mit Leerzeichen (0Byte mit grep -Z und xargs -0 verwenden)

grep -rlZ oldtext . --exclude-dir=.svn | xargs -0 sed -i 's/oldtext/newtext/g'
1
inetphantom

Verwendung der Kombination von grep und sed

for pp in $(grep -Rl looking_for_string)
do
    sed -i 's/looking_for_string/something_other/g' "${pp}"
done
1
Pawel

Wenn Sie dies verwenden möchten, ohne Ihr SVN-Repository vollständig zu zerstören, können Sie 'find' anweisen, alle versteckten Dateien zu ignorieren, indem Sie folgende Schritte ausführen:

find . \( ! -regex '.*/\..*' \) -type f -print0 | xargs -0 sed -i 's/subdomainA.example.com/subdomainB.example.com/g'
1
Marcus Floyd

Wenn Sie Zugriff auf den Knoten haben, können Sie einen npm install -g rexreplace ausführen und dann

rexreplace 'subdomainA.example.com' 'subdomainB.example.com' /home/www/**/*.*
0
mathiasrw

Dies ist die beste Allround-Lösung, die ich für OSX und Windows (msys2) gefunden habe. Sollte mit allem funktionieren, was die Gnu-Version von sed bekommen kann. Überspringt die .git-Verzeichnisse, damit Ihre Prüfsummen nicht beschädigt werden.

Installieren Sie auf einem Mac einfach zuerst coreutils und vergewissern Sie sich, dass gsed im Pfad ist.

brew install coreutils

Dann stecke ich diese Funktion in mein zshrc/bashrc ->

replace-recursive() {
    hash gsed 2>/dev/null && local SED_CMD="gsed" || SED_CMD="sed"
    find . -type f -name "*.*" -not -path "*/.git/*" -print0 | xargs -0 $SED_CMD -i "s/$1/$2/g"
}

usage: replace-recursive <find> <replace>
0
cchamberlain

So ersetzen Sie den gesamten Inhalt, der mit string_1 übereinstimmt, durch string_2 aller . C und . H Dateien im aktuellen Verzeichnis und in den Unterverzeichnissen (ohne .git /).

Dies funktioniert auf Mac :

find . -type f -path "*.git*" -Prune -o -name '*\.[ch]' -exec \
sed -i '' -e 's/'$1'/'$2'/g' {} +

Dies sollte unter Linux funktionieren (noch nicht getestet):

find . -type f -path "*.git*" -Prune -o -name '*\.[ch]' -exec \
sed -i 's/string_1/string_2/g' {} +
0
Klas. S

Eine einfachere Möglichkeit besteht darin, das Folgende in der Befehlszeile zu verwenden

find /home/www/ -type f|xargs Perl -pi -e 's/subdomainA\.example\.com/subdomainB.example.com/g' 
0
Vijay