it-swarm.com.de

Ändern Sie mehrere Dateien

Der folgende Befehl ändert den Inhalt von 2 Dateien korrekt.

sed -i 's/abc/xyz/g' xaa1 xab1 

Aber ich muss einige solcher Dateien dynamisch ändern und ich kenne die Dateinamen nicht. Ich möchte einen Befehl schreiben, der alle Dateien aus dem aktuellen Verzeichnis liest, beginnend mit xa* und sed sollte den Inhalt der Datei ändern.

151
shantanuo

Besser noch:

for i in xa*; do
    sed -i 's/asd/dfg/g' $i
done

weil niemand weiß, wie viele Dateien vorhanden sind, und es einfach ist, die Befehlszeilengrenzen zu überschreiten.

Folgendes passiert, wenn zu viele Dateien vorhanden sind:

# grep -c aaa *
-bash: /bin/grep: Argument list too long
# for i in *; do grep -c aaa $i; done
0
... (output skipped)
#
114
lenik

Ich bin überrascht, dass niemand das Argument -exec erwähnt hat, das für diese Art von Anwendungsfall vorgesehen ist, obwohl es für jeden übereinstimmenden Dateinamen einen Prozess startet:

find . -type f -name 'xa*' -exec sed -i 's/asd/dsg/g' {} \;

Alternativ könnte man xargs verwenden, wodurch weniger Prozesse aufgerufen werden:

find . -type f -name 'xa*' | xargs sed -i 's/asd/dsg/g'

Oder verwenden Sie einfach die +exec-Variante anstelle von ; in find, damit find mehr als eine Datei pro Aufruf des Unterprozesses bereitstellen kann:

find . -type f -name 'xa*' -exec sed -i 's/asd/dsg/g' {} +
115
ealfonso

Sie könnten grep und sed zusammen verwenden. Auf diese Weise können Sie Unterverzeichnisse rekursiv durchsuchen.

Linux: grep -r -l <old> * | xargs sed -i 's/<old>/<new>/g'
OS X: grep -r -l <old> * | xargs sed -i '' 's/<old>/<new>/g'

For grep:
    -r recursively searches subdirectories 
    -l prints file names that contain matches
For sed:
    -i extension (Note: An argument needs to be provided on OS X)
75
Raj

Diese Befehle funktionieren nicht in der Standardvariable sed von Mac OS X. 

Von man 1 sed:

-i extension
             Edit files in-place, saving backups with the specified
             extension.  If a zero-length extension is given, no backup 
             will be saved.  It is not recommended to give a zero-length
             extension when in-place editing files, as you risk corruption
             or partial content in situations where disk space is exhausted, etc.

Versucht

sed -i '.bak' 's/old/new/g' logfile*

und

for i in logfile*; do sed -i '.bak' 's/old/new/g' $i; done

Beide funktionieren gut.

26
funroll

@PaulR hat dies als Kommentar gepostet, aber die Leute sollten es als Antwort ansehen (und diese Antwort funktioniert am besten für meine Bedürfnisse):

sed -i 's/abc/xyz/g' xa*

Dies wird für eine moderate Anzahl von Dateien funktionieren, wahrscheinlich in der Größenordnung von zehn, aber wahrscheinlich nicht in der Größenordnung von Millionen .

14
palswim

Eine andere vielseitigere Methode ist die Verwendung von find:

sed -i 's/asd/dsg/g' $(find . -type f -name 'xa*')
6
dkinzer

Ich verwende find für eine ähnliche Aufgabe. Es ist ganz einfach: Sie müssen es als Argument für sed wie folgt übergeben:

sed -i 's/EXPRESSION/REPLACEMENT/g' `find -name "FILE.REGEX"`

Auf diese Weise müssen Sie keine komplexen Schleifen schreiben und es ist einfach zu sehen, welche Dateien Sie ändern möchten. Führen Sie einfach find aus, bevor Sie sed ausführen.

1
Bluesboy

Wenn Sie ein Skript ausführen können, habe ich Folgendes in einer ähnlichen Situation gemacht:

Mit einem dictionary/hashMap (assoziatives Array) und Variablen für den Befehl sed können wir das Array durchlaufen, um mehrere Zeichenfolgen zu ersetzen. Das Einfügen eines Platzhalters in name_pattern ermöglicht das Ersetzen von In-Place in Dateien durch ein Muster (dies könnte etwa name_pattern='File*.txt' sein) in einem bestimmten Verzeichnis (source_dir) . Alle Änderungen werden in logfile in destin_dir geschrieben

#!/bin/bash
source_dir=source_path
destin_dir=destin_path
logfile='sedOutput.txt'
name_pattern='File.txt'

echo "--Begin $(date)--" | tee -a $destin_dir/$logfile
echo "Source_DIR=$source_dir destin_DIR=$destin_dir "

declare -A pairs=( 
    ['WHAT1']='FOR1'
    ['OTHER_string_to replace']='string replaced'
)

for i in "${!pairs[@]}"; do
    j=${pairs[$i]}
    echo "[$i]=$j"
    replace_what=$i
    replace_for=$j
    echo " "
    echo "Replace: $replace_what for: $replace_for"
    find $source_dir -name $name_pattern | xargs sed -i "s/$replace_what/$replace_for/g" 
    find $source_dir -name $name_pattern | xargs -I{} grep -n "$replace_for" {} /dev/null | tee -a $destin_dir/$logfile
done

echo " "
echo "----End $(date)---" | tee -a $destin_dir/$logfile

Zuerst wird das Paare-Array deklariert, jedes Paar ist eine Ersetzungszeichenfolge, dann wird WHAT1 für FOR1 ersetzt und OTHER_string_to replace wird für string replaced in der Datei File.txt ersetzt. In der Schleife wird das Array gelesen, das erste Mitglied des Paares wird als replace_what=$i und das zweite als replace_for=$j abgerufen. Der Befehl find sucht im Verzeichnis nach dem Dateinamen (der einen Platzhalter enthalten kann), und der Befehl sed -i ersetzt in derselben Datei die zuvor definierten Dateien. Schließlich fügte ich eine grep hinzu, die in die Protokolldatei umgeleitet wurde, um die in der Datei (en) vorgenommenen Änderungen zu protokollieren. 

Dies funktionierte für mich in GNU Bash 4.3sed 4.2.2 und basierte auf der Antwort von VasyaNovikov für Loop over tupel in bash .

du kannst machen

' xxxx ' Text u suchen und ersetzen durch ' yyyy '

grep -Rn '**xxxx**' /path | awk -F: '{print $1}' | xargs sed -i 's/**xxxx**/**yyyy**/'
0
Mohamed Galal