So melden Sie "sed" -In-Place-Änderungen

23

Gibt sedes eine Möglichkeit, beim Ersetzen von Strings die vorgenommenen Änderungen zu protokollieren (ohne sich auf einen Unterschied zwischen alten und neuen Dateien zu verlassen)?

Wie kann ich zum Beispiel die Befehlszeile ändern?

find . -type f | xargs sed -i 's/abc/def/g'

kann ich die Änderungen sehen, die im laufenden Betrieb vorgenommen werden?

Ricab
quelle

Antworten:

15

Sie könnten nutzen sed‚s wFlagge mit entweder /dev/stderr, /dev/tty, /dev/fd/2wenn auf Ihrem System unterstützt. ZB mit einer Eingabe filewie:

foo first
second: missing
third: foo
none here

Laufen

sed -i '/foo/{
s//bar/g
w /dev/stdout
}' file

Ausgänge:

bar first
third: bar

obwohl der fileInhalt geändert wurde in:

bar first
second: missing
third: bar
none here

Also in Ihrem Fall läuft:

find . -type f -printf '\n%p:\n' -exec sed -i '/foo/{
s//bar/g
w /dev/fd/2
}' {} \;

bearbeitet die Dateien direkt und gibt sie aus:

./Datei1:
Bar Zeug
mehr bar

./Datei2:

./Datei3:
Bar zuerst
drittens: bar

Sie könnten auch etwas wie original line >>> modified linezB drucken :

find . -type f -printf '\n%p:\n' -exec sed -i '/foo/{
h
s//bar/g
H
x
s/\n/ >>> /
w /dev/fd/2
x
}' {} \;

bearbeitet die Dateien an Ort und Stelle und gibt Folgendes aus:

./Datei1:
Foo Stuff >>> Bar Stuff
mehr foo >>> mehr bar

./Datei2:

./Datei3:
Foo zuerst >>> Bar zuerst
drittens: foo >>> drittens: bar
don_crissti
quelle
16

Sie können dies in zwei Durchgängen tun, indem Sie die pRint-Aktion beim ersten Durchgang verwenden mit:

find . -type f | xargs sed --quiet 's/abc/def/gp'

Dabei zeigt --quietsed nicht jede Zeile an, und das pSuffix zeigt nur Zeilen an, bei denen die Ersetzung übereinstimmt.

Dies hat die Einschränkung, dass sed nicht anzeigt, welche Dateien geändert werden, was natürlich mit einigem zusätzlichen Aufwand behoben werden könnte.

msw
quelle
Es macht nicht ganz das, was ich wollte, da Sie zwei Durchgänge benötigen, aber ich stimme ab, weil ich gerade dieses P-Ding gelernt habe und es sehr nützlich ist. Vor allem, wenn, wie Sie sagten, die Dateien auch gedruckt wurden. So etwas wiefor x in `find . -type f`; do echo ///File $x: ; sed --quiet 's/abc/def/gp' $x; done
Ricab
@msw Ich habe viele sedAusdrücke, ich möchte nicht /gpfür jeden einen schreiben . Wie man es global einstellt?
Alhelal
8

Ich denke nicht, dass das möglich ist, aber eine Problemumgehung könnte darin bestehen, stattdessen Perl zu verwenden:

find . -type f | xargs perl -i -ne 's/abc/def/ && print STDERR' 

Dadurch werden die geänderten Zeilen auf Standardfehler gedruckt . Beispielsweise:

$ cat foo
fooabcbar
$ find . -type f | xargs perl -i -ne 's/abc/def/ && print STDERR' 
foodefbar

Sie können dies auch etwas komplexer gestalten, indem Sie die Zeilennummer, den Dateinamen, die ursprüngliche Zeile und die geänderte Zeile drucken:

$ find . -type f | 
   xargs perl -i -ne '$was=$_; chomp($was);
                      s/abc/def/ && print STDERR "$ARGV($.): $was : $_"' 
./foo(1): fooabcbar : foodefbar
terdon
quelle
+1 Sie können auch $ARGVfür den Namen der Datei verwenden, an der gearbeitet wird.
Joseph R.
@ JosephR. guter Punkt, fügte hinzu.
Terdon
Danke, das ist wahrscheinlich hilfreich (habe es selbst nicht getestet). Ich wähle nicht aus, da es sed nicht verwendet, also beantwortet es die Frage nicht genau.
Ricab
@ricab das ist in Ordnung, Sie sollten keine Antwort akzeptieren, es sei denn, es beantwortet tatsächlich Ihre Frage. Bedenken Sie jedoch, dass die perlSyntax für einfache Dinge wie diese der Syntax von 's sehr ähnlich ist, sedund ich glaube nicht, dass das, wonach Sie fragen, tatsächlich möglich ist sed.
terdon
3

Es ist möglich, das Flag w zu verwenden, das das aktuelle Muster in eine Datei schreibt. Indem wir es dem Ersetzungsbefehl hinzufügen, können wir aufeinanderfolgende Ersetzungen in einer Datei melden und es drucken, nachdem der Auftrag erledigt ist. Ich mag es auch, die ersetzte Zeichenfolge mit grep einzufärben.

sed -i -e "s/From/To/gw /tmp/sed.done" file_name
grep --color -e "To" /tmp/sed.done

Beachten Sie, dass zwischen dem w und dem Dateinamen nur ein Leerzeichen stehen darf .

Dies ist sogar besser als diff, da Unterschiede auch Änderungen zeigen können, wenn sie nicht von sed erstellt wurden.

Jack
quelle
Habe es gerade ausprobiert und sed schreibt nur Zeilen, für die es Substituenten gibt sed.done. Die Zeilen mit "To" in der Originaldatei werden daher nicht gedruckt sed.done, so dass Sie grep "To" sed.donenur die Zeilen sehen, die von geändert werden sed. Sie werden die ursprüngliche Zeile in der Datei nicht sehen, bevor sie ersetzt wurde, wenn Sie das anstreben ...
aairey
1

Ich mag die @ terdon-Lösung - dafür ist Perl gut.

Hier ist meine optimierte Version, die:

  • wird nicht versuchen, Dateien zu ändern, die nicht die passende Zeichenfolge haben
  • sichert die vorherige Version der Dateien, die sich ändern (erstellt eine .bak-Version)
  • listet alle geänderten Dateien / Leinen auf und zeigt die ALTEN und NEUEN Versionen dieser Zeile eingerückt und zum leichteren Lesen darunter an

Code

find /tmp/test -type f ! -name "*.bak" -exec grep -l '/opt/gridmon' {} \; | xargs -L1 perl -ni'.bak' -e'$old=$_; s/\/opt\/gridmon/~/g && print STDERR "$ARGV($.):\n\tOLD:$old\tNEW:$_"'

Beispielausgabe

/tmp/test/test4.cfg(13):
    OLD:    ENVFILE /opt/gridmon/server/etc/gridmonserver.cfg
    NEW:    ENVFILE ~/server/etc/gridmonserver.cfg
/tmp/test/test4.cfg(24):
    OLD:    ENVFILE /opt/gridmon/server/etc/gridmonserver.cfg
    NEW:    ENVFILE ~/server/etc/gridmonserver.cfg
andy987
quelle