Adicione texto antes da última ocorrência correspondida da palavra de pesquisa em um arquivo

4

Eu preciso adicionar algumas linhas de texto antes da última ocorrência de uma palavra (SearchWord no meu exemplo) em vários arquivos.

  • a pesquisa deve ser insensível a maiúsculas e minúsculas.
  • a palavra está presente em várias linhas do arquivo.

Dados existentes em um arquivo

SearchWord abc defgh
SEARCHWORD abc1234
sEarchWord abcd
sometext 1
sometext 2

Saída exigida

SearchWord abc defgh
SEARCHWORD abc1234 
NEWLINE_1_ADDED
NEWLINE_2_ADDED
NEWLINE_3_ADDED
sEarchWord abcd
sometext 1
sometext 2
    
por ksb 20.01.2017 / 15:53

3 respostas

3

Use ex , o editor de arquivos POSIX

ex é a forma não visual do editor de arquivos vi . Na verdade, é anterior a vi . vi (e Vim) ainda usam comandos ex ; sempre que você digitar dois pontos e digitar um comando, isso é um comando ex (embora o Vim tenha muitas extensões personalizadas).

Como ex abre o arquivo todo para leitura, em vez de operar em um fluxo, o endereçamento pode funcionar tanto para trás quanto para frente, diferente de Sed ou Awk.

Para essa edição específica, use o seguinte liner:

printf '%s\n' 'set ignorecase' '$+?searchword?i' 'NEWLINE_1_ADDED' 'NEWLINE_2_ADDED' 'NEWLINE_3_ADDED' . x | ex input.txt

Para testar o comando (imprimir na saída padrão em vez de salvar no arquivo), use %p em vez de x como o comando final:

printf '%s\n' 'set ignorecase' '$+?searchword?i' 'NEWLINE_1_ADDED' 'NEWLINE_2_ADDED' 'NEWLINE_3_ADDED' . %p | ex input.txt 

Para lidar com vários arquivos:

for f in *.txt; do
    printf '%s\n' 'set ignorecase' '$+?searchword?i' 'NEWLINE_1_ADDED' 'NEWLINE_2_ADDED' 'NEWLINE_3_ADDED' . x | ex "$f"
done

Explicação

Executando o comando printf sozinho, você pode ver os comandos que são passados para ex :

set ignorecase
$+?searchword?i
NEWLINE_1_ADDED
NEWLINE_2_ADDED
NEWLINE_3_ADDED
.
x

O comando set é autoexplicativo.

$+ refere-se à linha após a última linha, e ?...? significa pesquisar para trás a partir daí. (O + faz com que um arquivo com uma última linha correspondente seja tratado corretamente.) i significa "inserir" o texto antes da linha encontrada.

O . em uma linha, por si só, termina o texto a ser inserido.

x salva e sai.

    
por 20.01.2017 / 20:16
2

Mais fácil se você salvou essas linhas em um arquivo de texto, por exemplo infile , em seguida, usou ed para editar seus arquivos no local:

for f in ./*.txt; do
ed -s "$f" <<\IN
.t.
?[kK][eE][yY][wW][oO][rR][dD]?-1 r infile
$d
w
q
IN
done

Se você quiser apenas inserir algum texto (não o conteúdo de um arquivo), é semelhante:

for f in ./*.txt; do
ed -s "$f" <<\IN
.t.
?[kK][eE][yY][wW][oO][rR][dD]?-1 s/$\
some_text\
more_text\
last\_\&_line
$d
w
q
IN

Substitua w por ,p para ver o que faz sem modificar os arquivos.
Observe que barras invertidas, "e" comercial e delimitadores precisam ser escapados no RHS de uma substituição. O mesmo se aplica a novas linhas, exceto a última.
Como funciona ? Bem, duplique a última linha, procure sua palavra-chave para trás, insira o texto ou o conteúdo do arquivo antes dessa linha, exclua a última linha duplicada, escreva, saia.

Claro, se você não precisar editar esses arquivos e quiser apenas imprimir as versões modificadas, como Zanna , você não precisa de editores de texto arcaicos com uma sintaxe de outra pessoa. age ... Você pode fazer isso com uma única invocação sed - e porque Wildcard gentilmente me lembrou que nesta família de editores há um comando dedicado para i nsert texto antes de uma partida vamos usar isso e algumas ramificações:

sed -s '1{h;$!d;b end
}
/keyword/I{H;$!d
}
//{x;p;$!d
}
:end
${x;/keyword/Ii\
some_text_here\
more_text_here\
and_a\_backslash
}' ./*.txt

Isso só funciona com gnu sed embora ... com outros sed s você terá que executar um loop (desta vez sem a parte de ramificação para ter uma linha em branco entre o conteúdo de cada arquivo):

for f in ./*.txt; do
sed -s '/[kK][eE][yY][wW][oO][rR][dD]/{H;$!d
}
//{x;p;$!d
}
${x;/[kK][eE][yY][wW][oO][rR][dD]/i\
some_text_here\
more_text_here\
and_a\_backslash
}' "$f"
done

Como você pode ver mesmo com i nsert, ainda é preciso escapar das barras invertidas e das novas linhas (exceto a última). Fora isso, é simples: apenas acumula linhas no buffer de retenção e trocas ao encontrar uma correspondência; na última linha ele troca novamente e insere o texto antes da partida, se houver.

    
por 20.01.2017 / 21:24
2

Se você tiver tac e% GNUsed, poderá usar

$ tac file | sed '0,/searchword/I s/.*searchword.*/&\nNEWLINE_3_ADDED\nNEWLINE_2_ADDED\nNEWLINE_1_ADDED/I' | tac
SearchWord abc defgh
SEARCHWORD abc1234
NEWLINE_1_ADDED
NEWLINE_2_ADDED
NEWLINE_3_ADDED
sEarchWord abcd
sometext 1
sometext 2

Explicação

  • tac imprime o arquivo para trás
  • 0,/pattern/ operam na primeira ocorrência do padrão apenas
  • I procura insensível a maiúsculas
  • s/old/new/ replace old com new
  • .*searchword.* corresponde à linha inteira que contém searchword
  • & de todo o padrão correspondente em substituição
  • \n newline

Talvez mais legível:

tac file | sed '0,/searchword/I s/.*searchword.*/&\
NEWLINE_ADDED_3\
NEWLINE_ADDED-2\
NEWLINE_ADDED_1/I' | tac
    
por 20.01.2017 / 19:44