Ersetzen Sie mehrere Zeichenfolgen durch andere Zeichenfolgen in mehreren Dateien

7

Ich habe mehrere Textdateien. Alle diese Textdateien müssen einer Bearbeitungsreihe unterzogen werden, die in ausgeführt werden kann vim. Ich möchte das automatisieren. vimhat mehrere Befehle zum Ersetzen. Angenommen, die Textdateien müssen wie folgt ersetzt werden:

  1. Junge durch Junge ersetzen: %s/boy/Boy/g
  2. Mädchen durch Mädchen ersetzen: %s/girl/Girl/g
  3. Leerzeilen löschen: g/^$/d

Dies ist nur ein einfaches Beispiel. Gibt es eine Möglichkeit, all diese Regeln zu schreiben und diese dann für mehrere Dateien zu automatisieren?

Noor
quelle
Ich habe Ihre Frage von vi in ​​vim geändert, da ich davon ausgegangen bin, dass Sie diese verwendet haben. Bitte bestätigen Sie dies.
slm
@braiam - was ist mit vim?
slm
@slm Ich nehme an, das Tag ist genug ... der ursprüngliche Titel spiegelte nicht die Aufgabe wider und war tatsächlich irreführend.
Braiam

Antworten:

14

Ja, es gibt eine Möglichkeit, dies zu automatisieren. Und es beginnt mit der Auswahl des richtigen Werkzeugs für den Job.
In diesem Fall sollten Sie zB verwenden sedund nicht versuchen, zu biegen, viwas für den interaktiven Gebrauch (und nicht für die Automatisierung) ausgelegt ist.

Die Ersetzungssyntax für sedist weitgehend dieselbe wie für vi.

 sed -i.backup 's/boy/Boy/g' file-name-1 file-name-2 ...
Anthon
quelle
2
Anscheinend gibt es hier ein Echo, 4 andere wollten genau sagen, was Sie getan haben 8-)
slm
3
sedist ein Stream - Editor viist ein visueller Textdatei - Editor, gebaut oben auf ex/ ed(und der sBefehl ist ein exBefehl in vi). Die meisten sedImplementierungen haben keine -i. exund edsind Standard-Unix-Befehle. Ja, es kann mit einigen Sed-Implementierungen durchgeführt werden, aber das heißt nicht, dass es das richtige Werkzeug ist.
Stéphane Chazelas
3
Ich habe nicht geschrieben, sedist "das richtige Werkzeug". Es gibt mehrere Werkzeuge, daher das "zB". Soweit ich mich erinnere, edwurden nicht mehrere Dateien bearbeitet, und Sie mussten Lese- und Schreibvorgänge für das Standardprogramm ausführen. Das letzte Mal, an das ich mich erinnern kann, dass ich es verwendet habe, edwar, dass vies Mitte der 80er Jahre auf einem PDP-11 nicht verfügbar war, sodass ich möglicherweise mit seinen Funktionen nicht mehr auf dem neuesten Stand bin
Anthon,
12

Vim-Skripte (gvim, vim) können elegant sein und sind sehr einfach anzupassen

vi -s edit.vim  test.txt

wo edit.vim enthält (das: wq ist optional)

:%s/boy/Boy/g
:%s/girl/Girl/g
:g/^$/d
:wq

wo test.txt enthält

boys & girls

boys & girls
boy & girl

boys & girls

Hier ist ein generisches Vim-Skript zum Bereinigen einer "mucky" Textdatei

:" clean.vim
:" clean up a text file
:" delete DOS returns (actually superfluous because of non-ASCII
deletion)
:%s/\r//eg
:" delete any non-ASCII including invisibles
:%s/[\x00-\x1f\x80-\xff]/ /eg
:" compress multiple spaces
:%s/\s\s\+/ /eg
:" delete end of line spaces
:%s/\s\+$//e
:" compress multiple blank lines
:v/./,/./-j
:" sort eliminating duplicate lines
:%sort -u

Denken Sie daran, dass Sie das Skript auch aus vim heraus beziehen können

:source clean.vim
zzapper
quelle
3

Warum nicht verwenden sed?

Mit sed können Sie leicht die Dateien im Verzeichnis durchlaufen:

for f in *.txt
do
sed 's/boy/Boy/g;s/girl/Girl/g;/^\s*$/d' $f > tmp
mv tmp $f
done

Das obige Beispiel würde Junge-> Junge, Mädchen-> Mädchen ändern und die Zeilen leer löschen.

janrpn
quelle
2
Sie müssen die for-Schleife nicht ausführen, sondern geben sed eine Liste von Dateien direkt wie in Anthons Antwort unten oder dem Globus * .txt direkt. Auch das mvist unnötig. Verwenden Sie sedden -i.extSchalter, um ein Backup zu erstellen.
slm
1
Ich kannte diesen Trick nicht. Ich werde versuchen, mich das nächste Mal daran zu erinnern. Vielen Dank für den Tipp und die Erklärung @slm
janrpn
2

Und ein Inline- viSkript:

$ vi test.txt -c '%s/boy/Boy/g | %s/girl/Girl/g | g/^$/d | wq'
garethTheRed
quelle
2

HINWEIS: Die Verwendung sedist der geeignete Weg, um dies zu tun, wie aus der Antwort von @ Anthon hervorgeht !


Nur um Ihnen zu zeigen, wie Sie dies tun können vim. Sie können den argdoBefehl innerhalb verwenden vim. Von der :help argdoin vim:

:argdo[!] {cmd}         Execute {cmd} for each file in the argument list.
Beispiel:
    :args *.c
    :argdo set ff=unix | update

Dies setzt die Option 'Dateiformat' auf "Unix" und schreibt die Datei, wenn sie jetzt geändert wird. Dies erfolgt für alle *.cDateien.

Beispiel:
    :args *.[ch]
    :argdo %s/\<my_foo\>/My_Foo/ge | update

Dadurch wird das Wort "my_foo" in allen *.cund *.hDateien in "My_Foo" geändert . Das "e" :substitute-Flag wird für den Befehl verwendet, um einen Fehler für Dateien zu vermeiden, in denen "my_foo" nicht verwendet wird. :updateschreibt die Datei nur, wenn Änderungen vorgenommen wurden.

Der Befehl gibt an :args <pattern>, für :argdowelche Dateien Sie Folgendes ausführen möchten {cmd}. Einmal definiert, führen Sie aus, :argdo {cmd} | updatewo {cmd}Ihre s/boy/Boy/gSubstitution sein kann.

Werden alle Dateien während dieses Vorgangs geöffnet?

Es scheint so. Ich habe den folgenden Test durchgeführt, um dies zu bestätigen.

$ for i in {1..3};do echo "dog" > file${i}.txt;done

$ head file*.txt
==> file1.txt <==
dog

==> file2.txt <==
dog

==> file3.txt <==
dog

Gehen Sie nun zu vimund führen Sie die folgenden Befehle aus:

:args file*.txt

Sie können bestätigen, dass alle geöffnet wurden:

:ls
  2 %a   "file1.txt"                    line 1
  3      "file2.txt"                    line 0
  4      "file3.txt"                    line 0

Tun Sie :argdo ...:

:argdo %s/dog/cat/g | update
"file1.txt" 1L, 4C written
"file2.txt" 1L, 4C written
"file3.txt" 1L, 4C written

Denken Sie also daran, wenn Sie diesen Ansatz verwenden möchten. Es öffnet ineffizient jede Datei, die Ihrem Muster entspricht, :argsund wendet den Befehl nacheinander auf sie an.

slm
quelle
0

sedist das Werkzeug für Sie. Die meisten Befehle, die Sie verwenden viwerden, sind in verfügbar, sedund daher gibt es nicht viel Lernkurve. Die Befehle vi, die mit a gestartet :werden, basieren auf dem zugrunde liegenden Editor edund sind auch in verfügbar sed. Sie verwenden diese Befehle ohne :und werden standardmäßig auf die gesamte Datei angewendet. Zum Beispiel werden Sie für Ihr Beispiel tun

sed -i 's/boy/Boy/g
s/girl/Girl/g
/^$/d' file1 file2 ...
unxnut
quelle
0

Angenommen, Sie visind tatsächlich vimoder gvim, es kann sicherlich in Vimscript implementiert werden, aber es kann kompliziert sein, mehrere Dateien gleichzeitig zu bearbeiten.

Besser das richtige Werkzeug für den Job: sedDer Stream-Editor, der eine sehr ähnliche Syntax wie verwendet vi, aber dazu gedacht ist, Dateien zeilenweise als Stream ohne Interaktion zu bearbeiten.

Mit der Option -i, sedkönnen Sie Änderungen in mehreren Dateien. -i.backup moves an original filefoo.txt tofoo.txt.backup , and applies changes in place tofoo.txt`:

sed -i.backup -e 's/boy/Boy/g' -e 's/girl/Girl/g' -e '/^$/d' f1.txt f2.txt ...

Sie können dies auch innen ausführen vimit :!:

:!sed -i.backup -e 's/boy/Boy/g' -e 's/girl/Girl/g' -e '/^$/d' f1.txt f2.txt ...
Volker Siegel
quelle
0

Ich stimme @ Anthons Antwort zu . Verwendung sed:

$ sed -e "s/boy/Boy/g" -e "s/girl/Girl/g" -e '/^\s*$/d' file

HINWEIS: (Dies löscht auch Zeilen mit nur Leerzeichen.)

Fügen Sie hinzu -i, sedwenn Sie direkt ersetzen möchten (Ihre Dateien unterliegen der Versionskontrolle, oder?). Für mehrere Dateien:

$ for $f in $(ls mydir); do
   sed -e "s/boy/Boy/g" -e "s/girl/Girl/g" -e '/^\s*$/d' $f
done
Rolf
quelle
0

Alternativen

Es sei denn , Sie wirklich besondere Vim - Fähigkeiten benötigen, sind Sie wahrscheinlich besser mit nicht-interaktive Tools wie sed, awkoder Perl / Python / Ruby - / Ihre Lieblingsskriptsprache hier .

Sie können Vim jedoch nicht interaktiv verwenden:

Silent Batch-Modus

Verwenden Sie für eine sehr einfache Textverarbeitung (dh die Verwendung von Vim wie ein erweitertes 'sed' oder 'awk', das möglicherweise nur von den erweiterten regulären Ausdrücken in einem :substituteBefehl profitiert ) den Ex-Modus .

REM Windows
call vim -N -u NONE -n -es -S "commands.ex" "filespec"

Hinweis: Der stille Batch-Modus ( :help -s-ex) bringt die Windows-Konsole durcheinander, sodass Sie möglicherweise clsnach dem Ausführen von Vim eine Bereinigung durchführen müssen.

# Unix
vim -T dumb --noplugin -n -es -S "commands.ex" "filespec"

Achtung: Vim wartet auf Eingabe, wenn die "commands.ex"Datei nicht existiert. besser vorher auf seine Existenz prüfen! Alternativ kann Vim die Befehle von stdin lesen. Sie können auch einen neuen Puffer mit aus stdin gelesenem Text füllen und Befehle aus stderr lesen, wenn Sie das -Argument verwenden.

Vollautomatisierung

Verwenden Sie für eine erweiterte Verarbeitung mit mehreren Fenstern und eine echte Automatisierung von Vim (bei der Sie möglicherweise mit dem Benutzer interagieren oder Vim laufen lassen, damit der Benutzer die Kontrolle übernimmt):

vim -N -u NONE -n -c "set nomore" -S "commands.vim" "filespec"

Hier ist eine Zusammenfassung der verwendeten Argumente:

-T dumb           Avoids errors in case the terminal detection goes wrong.
-N -u NONE        Do not load vimrc and plugins, alternatively:
--noplugin        Do not load plugins.
-n                No swapfile.
-es               Ex mode + silent batch mode -s-ex
                Attention: Must be given in that order!
-S ...            Source script.
-c 'set nomore'   Suppress the more-prompt when the screen is filled
                with messages or output to avoid blocking.
Ingo Karkat
quelle