Como substituir uma string na 5ª linha de vários arquivos de texto?

22

Quero substituir a 5ª linha de vários arquivos de texto ( arquivo1.txt, arquivo2.txt, arquivo3.txt, arquivo4.txt ) pela string "Good Morning" usando um único comando de terminal.

Todos os arquivos de texto estão localizados no meu ~/Desktop.

Nota: Minha área de trabalho é composta por 6 arquivos .txt. Quero aplicar a alteração apenas aos 4 arquivos de texto mencionados acima.

Avinash Raj
fonte

Respostas:

40

Aqui estão algumas abordagens. Estou usando a chave de expansão ( file{1..4}.txt), o que significafile1.txt file2.txt file3.txt file4.txt

  1. Perl

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

    Explicação:

    • -i: causa perla edição dos arquivos no local, alterando o arquivo original.

      Se -ifor seguido com um sufixo de extensão de arquivo, um backup será criado para cada arquivo modificado. Ex: -i.bakcria um file1.txt.bakif file1.txtmodificado durante a execução.

    • -p: significa ler o arquivo de entrada linha por linha, aplicar o script e imprimi-lo.

    • -e: permite passar um script na linha de comando.

    • s/.*/ Good Morning /: Isso substituirá o texto na linha atual ( .*) por Bom dia.

    • $.é uma variável Perl especial que mantém o número da linha atual do arquivo de entrada. Então s/foo/bar/ if $.==5,, significa substituir foopor barapenas na 5ª linha.

  2. sed

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

    Explicação:

    • -i: Por exemplo perl, edite o arquivo no local.

    Por padrão, sedimprime cada linha do arquivo de entrada. O 5s/pattern/replacement/padrão de substituição de meios com substituição na 5ª linha.

  3. Awk

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

    Explicação:

    awknão tem equivalente à -iopção¹, o que significa que precisamos criar um arquivo temporário ( foobar) que é renomeado para substituir o original. O loop bash for f in file{1..4}.txt; do ... ; donesimplesmente passa por cada um deles file{1..4}.txt, salvando o nome do arquivo atual como $f. In awk, NRé o número da linha atual e $0o conteúdo da linha atual. Portanto, o script substituirá a linha ( $0) por "Bom dia" apenas na 5ª linha. 1;é awkpara "imprimir a linha".

    ¹ As versões mais recentes não como devnull mostrou em sua resposta .

  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 

    Explicação:

    O loop é explicado na seção anterior.

    • head -4: imprime as 4 primeiras linhas

    • echo " Good Morning ": imprimir "bom dia"

    • tail -n +6: imprima tudo da 6ª linha até o final do arquivo

    Os parênteses em ( )torno desses três comandos permitem capturar a saída de todas as três (portanto, primeiras 4 linhas, depois "Bom dia" e depois o restante das linhas) e redirecioná-las para um arquivo.

Terdon
fonte
23

Você poderia usar sed:

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

acrescentaria Good morningna quinta linha de um arquivo.

Se você deseja substituir o conteúdo da linha 5, diga:

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

Se você deseja salvar as alterações no arquivo no local, use a -iopção:

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

sedpode lidar com mais de um arquivo por vez. Você pode apenas adicionar mais nomes de arquivo ao final do comando. Você também pode usar expansões do bash para corresponder a arquivos específicos:

# 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

Você pode ler mais sobre expansões de chaves aqui .


As versões 4.1.0 e superior do GNU awk vêm com uma extensão que permite a edição no local. Então você poderia dizer:

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

para substituir a linha 5 do arquivo por Good morning!

devnull
fonte
2

Eu não vi a solução python, então aqui está:

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)

A idéia básica é que, para cada arquivo fornecido na linha de comando como argumento, gravemos um arquivo temporário e enumeremos cada linha no arquivo original. Se o índice da linha for 5, escrevemos uma linha diferente. O restante está apenas substituindo o arquivo antigo pelo arquivo temporário 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

O mesmo pode ser alcançado com a compreensão da lista. Abaixo está uma linha do mesmo script:

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

ou sem 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

O que resta é simplesmente redirecionar a saída do conteúdo editado para outro arquivo com > output.txt

Sergiy Kolodyazhnyy
fonte
1

Você pode usar o Vim no modo Ex:

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

  2. c substituir texto

  3. x escreva se as alterações foram feitas (elas têm) e saia

Steven Penny
fonte
0

A seguir, um script bash que encerra o processo perl sugerido nesta resposta

Exemplo:

/ tmp / it

console:
  enabled:false

Em seguida, execute o seguinte:

$ search_and_replace_on_line_of_file.sh false true 2 /tmp/it

e o arquivo agora contém

/ 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
fonte