Jak zamienić ciąg w 5. linii wielu plików tekstowych?

22

Chcę zastąpić 5. wiersz wielu plików tekstowych ( plik1.txt, plik2.txt, plik3.txt, plik4.txt ) ciągiem „Dzień dobry” za pomocą jednego polecenia terminalu.

Wszystkie pliki tekstowe znajdują się na moim ~/Desktop.

Uwaga: Mój pulpit składa się z 6 plików .txt. Chcę zastosować zmianę tylko do wyżej wymienionych 4 plików tekstowych.

Avinash Raj
źródło

Odpowiedzi:

40

Oto kilka podejść. Korzystam z Brace Expansion ( file{1..4}.txt), co oznaczafile1.txt file2.txt file3.txt file4.txt

  1. Perl

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

    Wyjaśnienie:

    • -i: powoduje perledycję plików na miejscu, zmieniając oryginalny plik.

      Jeśli -iwystępuje po nim sufiks rozszerzenia pliku, tworzona jest kopia zapasowa dla każdego modyfikowanego pliku. Ex: -i.baktworzy file1.txt.bakif file1.txtjest modyfikowany podczas wykonywania.

    • -p: oznacza odczytanie pliku wejściowego wiersz po wierszu, zastosowanie skryptu i wydrukowanie go.

    • -e: umożliwia przekazanie skryptu z wiersza polecenia.

    • s/.*/ Good Morning /: To zastąpi tekst w bieżącej linii ( .*) tekstem „ Dzień dobry”.

    • $.to specjalna zmienna Perla, która przechowuje bieżący numer wiersza pliku wejściowego. Tak więc s/foo/bar/ if $.==5, środki zastąpić fooze bartylko na 5 linii.

  2. sed

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

    Wyjaśnienie:

    • -i: Jak dla perl, edytuj plik na miejscu.

    Domyślnie seddrukuje każdą linię pliku wejściowego. 5s/pattern/replacement/Wzór środek zastępczy z wymianą na 5 linii.

  3. Awk

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

    Wyjaśnienie:

    awknie ma odpowiednika z -iopcją¹, co oznacza, że ​​musimy utworzyć plik tymczasowy ( foobar), który następnie zostanie przemianowany w celu zastąpienia oryginału. Pętla bash for f in file{1..4}.txt; do ... ; donepo prostu przechodzi przez każdą z nich file{1..4}.txt, zapisując bieżącą nazwę pliku jako $f. W awk, NRjest bieżącym numerem wiersza i $0jest zawartością bieżącego wiersza. Tak więc skrypt zastąpi wiersz ( $0) słowem „Dzień dobry” tylko w piątym wierszu. 1;oznacza awk„wydrukuj linię”.

    ¹ Nowsze wersje działają tak, jak pokazał devnull w swojej odpowiedzi .

  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 

    Wyjaśnienie:

    Pętla została wyjaśniona w poprzedniej sekcji.

    • head -4: wydrukuj pierwsze 4 linie

    • echo " Good Morning ": wydrukuj „Dzień dobry”

    • tail -n +6: wydrukuj wszystko, od szóstej linii do końca pliku

    Nawiasy ( )wokół tych trzech poleceń pozwalają uchwycić dane wyjściowe wszystkich trzech (więc pierwsze 4 wiersze, potem „Dzień dobry”, a następnie pozostałe wiersze) i przekierować je do pliku.

terdon
źródło
23

Możesz użyć sed:

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

dołącza Good morningsię do piątej linii pliku.

Jeśli zamiast tego chcesz zastąpić zawartość wiersza 5, powiedz:

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

Jeśli chcesz zapisać zmiany w pliku w miejscu, użyj -iopcji:

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

sedmoże obsługiwać więcej niż jeden plik na raz. Możesz po prostu dodać więcej nazw plików na końcu polecenia. Możesz także użyć rozszerzeń bash, aby dopasować określone pliki:

# 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

Możesz przeczytać więcej o rozszerzeniach nawiasów tutaj .


GNU awk w wersji 4.1.0 i nowszych zawiera rozszerzenie umożliwiające edycję w miejscu. Więc możesz powiedzieć:

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

zamienić wiersz # 5 w pliku na Good morning!

diabelnie
źródło
2

Nie widziałem rozwiązania w języku Python, więc oto:

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)

Podstawową ideą jest to, że dla każdego pliku podanego w wierszu polecenia jako argument zapisujemy plik tymczasowy i wyliczamy każdy wiersz w oryginalnym pliku. Jeśli indeks linii wynosi 5, wypisujemy inną linię. Reszta to tylko zamiana starego pliku na plik tymczasowy 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

To samo można osiągnąć dzięki zrozumieniu listy. Poniżej znajduje się jedna linijka tego samego skryptu:

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

lub bez 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

Pozostaje po prostu przekierować wyjście edytowanej treści do innego pliku za pomocą > output.txt

Sergiy Kolodyazhnyy
źródło
1

Możesz używać Vima w trybie Ex:

for b in 1 2 3 4
do
  ex -sc '5c|Good Morning' -cx file"$b".txt
done
  1. 5 wybierz 5. linię

  2. c zamień tekst

  3. x napisz, jeśli zmiany zostały wprowadzone (mają) i wyjdź

Steven Penny
źródło
0

Poniżej znajduje się skrypt bash, który kończy proces Perla sugerowany w tej odpowiedzi

Przykład:

/ tmp / it

console:
  enabled:false

Następnie uruchom następujące czynności:

$ search_and_replace_on_line_of_file.sh false true 2 /tmp/it

a plik zawiera teraz

/ 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 parki
źródło