Come sostituire una stringa sulla 5a riga di più file di testo?


22

Voglio sostituire la 5a riga di più file di testo ( file1.txt, file2.txt, file3.txt, file4.txt ) con la stringa "Good Morning" usando un singolo comando terminale.

Tutti i file di testo si trovano sul mio ~/Desktop.

Nota: il mio desktop è composto da 6 file .txt. Voglio applicare la modifica solo ai 4 file di testo sopra menzionati.

Risposte:


40

Ecco alcuni approcci. Sto usando parentesi graffe di espansione ( file{1..4}.txt) che significafile1.txt file2.txt file3.txt file4.txt

  1. Perl

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

    Spiegazione:

    • -i: provoca la perlmodifica dei file in atto, modificando il file originale.

      Se -iviene seguito con un suffisso di estensione file, viene creato un backup per ogni file che viene modificato. Es: -i.bakcrea un file1.txt.bakif che file1.txtviene modificato durante l'esecuzione.

    • -p: significa leggere il file di input riga per riga, applicare lo script e stamparlo.

    • -e: consente di passare uno script dalla riga di comando.

    • s/.*/ Good Morning /: Sostituirà il testo nella riga corrente ( .*) con Good Morning.

    • $.è una variabile Perl speciale che contiene il numero di riga corrente del file di input. Quindi, s/foo/bar/ if $.==5significa sostituire foocon barsolo sulla 5a riga.

  2. sed

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

    Spiegazione:

    • -i: Ad esempio perl, modifica il file in atto.

    Per impostazione predefinita, sedstampa ogni riga del file di input. Il 5s/pattern/replacement/mezzo sostituisce il motivo con la sostituzione sulla 5a riga.

  3. awk

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

    Spiegazione:

    awknon ha equivalenti -iall'opzione¹, il che significa che dobbiamo creare un file temporaneo ( foobar) che viene quindi rinominato per sovrascrivere l'originale. Il loop bash for f in file{1..4}.txt; do ... ; donepassa semplicemente attraverso ciascuno di file{1..4}.txt, salvando il nome del file corrente come $f. In awk, NRè il numero di riga corrente ed $0è il contenuto della riga corrente. Quindi, lo script sostituirà la riga ( $0) con "Good Morning" solo sulla 5a riga. 1;è awkper "stampare la riga".

    ¹ Le versioni più recenti fanno come mostrato da devnull nella sua risposta .

  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 

    Spiegazione:

    Il ciclo è spiegato nella sezione precedente.

    • head -4: stampa le prime 4 righe

    • echo " Good Morning ": stampa "Buongiorno"

    • tail -n +6: stampa tutto dalla sesta riga alla fine del file

    Le parentesi ( )attorno a questi tre comandi consentono di catturare l'output di tutte e tre (quindi, 1a 4 righe, quindi "Buongiorno", quindi il resto delle righe) e reindirizzarle su un file.


23

Puoi usare sed:

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

aggiungerei Good morning sulla quinta riga di un file.

Se si desidera sostituire i contenuti nella riga 5, invece:

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

Se si desidera salvare le modifiche sul file sul posto, utilizzare l' -iopzione:

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

sedpuò gestire più di un file alla volta. Puoi semplicemente aggiungere altri nomi di file alla fine del comando. Puoi anche usare le espansioni di bash per abbinare particolari file:

# 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

Puoi leggere di più sulle espansioni di parentesi graffe qui .


Le versioni 4.1.0 e successive di GNU awk vengono fornite con un'estensione che consente la modifica sul posto. Quindi potresti dire:

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

per sostituire la riga # 5 nel file con Good morning!


2

Non ho visto la soluzione Python quindi eccola:

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)

L'idea di base è che per ogni file fornito sulla riga di comando come argomento, scriviamo un file temporaneo ed enumeriamo ogni riga nel file originale. Se l'indice della riga è 5, scriviamo una riga diversa. Il resto sta semplicemente sostituendo il vecchio file con il file temporaneo 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

Lo stesso si può ottenere con la comprensione dell'elenco. Di seguito è riportata una riga della stessa sceneggiatura:

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)])'

o senza 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

Ciò che rimane è semplicemente reindirizzare l'output dei contenuti modificati in un altro file con > output.txt


1

Puoi usare Vim in modalità Ex:

for b in 1 2 3 4
do
  ex -sc '5c|Good Morning' -cx file"$b".txt
done
  1. 5 selezionare la 5a riga

  2. c sostituire il testo

  3. x scrivere se sono state apportate modifiche (hanno) ed uscire


0

Quello che segue è uno script bash che avvolge il processo perl suggerito in questa risposta

Esempio:

/ Tmp / it

console:
  enabled:false

Quindi eseguire quanto segue:

$ search_and_replace_on_line_of_file.sh false true 2 /tmp/it

e il file ora contiene

/ 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 
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.