Wie ersetze ich eine Zeichenfolge in der 5. Zeile mehrerer Textdateien?

22

Ich möchte die 5. Zeile mehrerer Textdateien ( file1.txt, file2.txt, file3.txt, file4.txt ) mit einem einzigen Terminalbefehl durch die Zeichenfolge "Good Morning" ersetzen .

Alle Textdateien befinden sich auf meiner ~/Desktop.

Hinweis: Mein Desktop besteht aus 6 TXT-Dateien. Ich möchte die Änderung nur auf die oben genannten 4 Textdateien anwenden.

Avinash Raj
quelle

Antworten:

40

Hier sind einige Ansätze. Ich verwende Klammererweiterung ( file{1..4}.txt), was bedeutetfile1.txt file2.txt file3.txt file4.txt

  1. Perl

    perl -i -pe 's/.*/ Good Morning / if $.==5' file{1..4}.txt

    Erläuterung:

    • -i: Bewirkt perl, dass die Dateien an Ort und Stelle bearbeitet werden und die ursprüngliche Datei geändert wird.

      Wenn -iein Dateierweiterungssuffix folgt, wird für jede Datei, die geändert wird, eine Sicherungskopie erstellt. Beispiel: -i.bakErstellt ein file1.txt.bakif, file1.txtdas während der Ausführung geändert wird.

    • -p: bedeutet, die Eingabedatei zeilenweise zu lesen, das Skript anzuwenden und auszudrucken.

    • -e: Ermöglicht die Übergabe eines Skripts über die Befehlszeile.

    • s/.*/ Good Morning /: Dadurch wird der Text in der aktuellen Zeile ( .*) durch Guten Morgen ersetzt.

    • $.ist eine spezielle Perl-Variable, die die aktuelle Zeilennummer der Eingabedatei enthält. Also, s/foo/bar/ if $.==5bedeutet ersetzen foomit barnur in der 5. Zeile.

  2. sed

    sed -i '5s/.*/ Good Morning /' file{1..4}.txt

    Erläuterung:

    • -i: perlBearbeiten Sie die Datei wie für .

    Standardmäßig wird sedjede Zeile der Eingabedatei gedruckt. Das 5s/pattern/replacement/bedeutet Ersatzmuster mit Ersetzung in der 5. Zeile.

  3. Awk

    for f in file{1..4}.txt; do 
        awk 'NR==5{$0=" Good Morning "}1;' "$f" > foobar && mv foobar "$f"; 
    done

    Erläuterung:

    awkEntspricht nicht der -iOption¹, was bedeutet, dass eine temporäre Datei ( foobar) erstellt werden muss, die dann umbenannt wird, um das Original zu überschreiben. Die Bash-Schleife for f in file{1..4}.txt; do ... ; donedurchläuft einfach jeden von ihnen file{1..4}.txtund speichert den aktuellen Dateinamen unter $f. In awk, NRist die aktuelle Zeilennummer und $0der Inhalt der aktuellen Zeile. Das Skript ersetzt die Zeile ( $0) nur in der 5. Zeile durch "Good Morning". 1;ist awkfür "print the line".

    ¹ Neuere Versionen entsprechen den Angaben in seiner Antwort .

  4. coreutils

    for f in file{1..4}.txt; do 
        (head -4 "$f"; echo " Good Morning "; tail -n +6 "$f") > foobar && 
        mv foobar "$f"; 
    done 

    Erläuterung:

    Die Schleife wird im vorherigen Abschnitt erläutert.

    • head -4: drucke die ersten 4 Zeilen

    • echo " Good Morning ": print "Guten Morgen"

    • tail -n +6: drucke alles von der 6. Zeile bis zum Ende der Datei

    Die Klammern ( )um diese drei Befehle ermöglichen es Ihnen, die Ausgabe aller drei (also der ersten vier Zeilen, dann "Guten Morgen" und dann der Rest der Zeilen) zu erfassen und in eine Datei umzuleiten.

terdon
quelle
23

Sie könnten verwenden sed:

sed '5s/^/Good morning /' file

würde Good morningin der fünften Zeile einer Datei anhängen .

Wenn Sie stattdessen den Inhalt in Zeile 5 ersetzen möchten, sagen Sie:

sed '5s/.*/Good morning/' file

Wenn Sie die Änderungen an der Datei direkt speichern möchten, verwenden Sie die -iOption:

sed -i '5s/.*/Good morning/' file

sedkann mehr als eine Datei gleichzeitig verarbeiten. Sie können am Ende des Befehls einfach weitere Dateinamen hinzufügen. Sie können auch Bash-Erweiterungen verwenden, um bestimmte Dateien abzugleichen:

# manually specified
sed -i '5s/.*/Good morning/' file1.txt file2.txt file3.txt file4.txt

# wildcard: all files on the desktop
sed -i '5s/.*/Good morning/' ~/Desktop/*

# brace expansion: file1.txt, file2.txt, file3.txt, file4.txt
sed -i '5s/.*/Good morning/' file{1..4}.txt

Sie können mehr über Klammer Erweiterungen lesen .


GNU awk-Versionen 4.1.0 und höher verfügen über eine Erweiterung , die die direkte Bearbeitung ermöglicht. Man könnte also sagen:

gawk -i inplace 'NR==5{$0="Good morning"}7' file

um Zeile 5 in der Datei durch Good morning! zu ersetzen.

devnull
quelle
2

Ich habe keine Python-Lösung gesehen, daher hier:

import sys
import os
def open_and_replace(filename):

    with open(filename) as read_file:
        temp = open("/tmp/temp.txt","w")
        for index,line in enumerate(read_file,1):
            if  index == 5:
                temp.write("NEW STRING\n")
            else:
                temp.write(line.strip() + "\n")
        temp.close()
    os.rename("/tmp/temp.txt",filename)

for file_name in sys.argv[1:]:
    open_and_replace(file_name)

Die Grundidee ist, dass für jede Datei, die in der Befehlszeile als Argument angegeben wird, eine temporäre Datei geschrieben und jede Zeile in der Originaldatei aufgelistet wird. Wenn der Index der Zeile 5 ist, schreiben wir eine andere Zeile aus. Der Rest ersetzt nur die alte Datei durch die temporäre Datei Demo:

$> ls
file1.txt  file2.txt  file3.txt
$> cat file1.txt                                                               
line 1
line 2
line 3
line 4
GOOD MORNING
line 6
$> python ~/replace_5th_line.py file1.txt file2.txt  file3.txt                 
$> cat file1.txt                                                               
line 1
line 2
line 3
line 4
NEW STRING
line 6
$> cat file2.txt                                                               
line 1
line 2
line 3
line 4
NEW STRING
line 6

Dasselbe kann mit Listenverständnis erreicht werden. Unten ist ein Einzeiler des gleichen Skripts:

cat /etc/passwd | python -c 'import sys; print "\n".join(["CUSTOM"  if index == 5 else line.strip() for index,line in enumerate(sys.stdin,1)])'

oder ohne cat

python -c 'import sys; print "\n".join(["CUSTOM"  if index == 5 else line.strip() for index,line in enumerate(sys.stdin,1)])' < /etc/passwd

Was bleibt, ist einfach die Ausgabe von bearbeiteten Inhalten in eine andere Datei mit umzuleiten > output.txt

Sergiy Kolodyazhnyy
quelle
1

Sie können Vim im Ex-Modus verwenden:

for b in 1 2 3 4
do
  ex -sc '5c|Good Morning' -cx file"$b".txt
done
  1. 5 Wählen Sie die 5. Zeile

  2. c Text ersetzen

  3. x Schreiben Sie, wenn Änderungen vorgenommen wurden und beenden Sie das Programm

Steven Penny
quelle
0

Das folgende Bash-Skript fasst den in dieser Antwort vorgeschlagenen Perl-Prozess zusammen

Beispiel:

/ tmp / it

console:
  enabled:false

Führen Sie dann Folgendes aus:

$ search_and_replace_on_line_of_file.sh false true 2 /tmp/it

und die Datei enthält jetzt

/ tmp / it

console:
  enabled:true

search_and_replace_on_line_of_file.sh

function show_help()
{
  IT=$(CAT <<EOF

  usage: SEARCH REPLACE LINE_NUM FILE

  e.g. 

  false true 52 /tmp/it  -> replaces false with true on line 52 of file /tmp/it

  )
  echo "$IT"
  exit
}

if [ "$1" == "help" ]
then
  show_help
fi
if [ -z "$4" ]
then
  show_help
fi

SEARCH_FOR=$1
REPLACE_WITH=$2
LINE_NO=$3
FILE_NAME=$4
perl -i -pe "s/$SEARCH_FOR/$REPLACE_WITH/ if $.==$LINE_NO" $FILE_NAME 
Brad Parks
quelle