
Pesquise no arquivo a última linha com uma determinada String para adicionar uma linha após essa correspondência. Exemplo:
Entrada:
Apple
Banana
Apple
Orange
Apple
Grapefruit
Saída:
Apple
Banana
Apple
Orange
Apple
I am new string replaced at last apple
Grapefruit
Responder1
Com o GNU sed
você pode fazer isso:
sed ':a;N;$! ba;s/.*\nApple/&\nYour appended line/'
O padrão :a;N;$! ba
anexa linhas do arquivo ao buffer N
enquanto o final do arquivo ( $
endereço) não é ( !
) alcançado (pular para :a
com ba
). Portanto, se você sair do loop, todas as linhas estarão no buffer e o padrão de pesquisa .*\nApple
corresponderá ao arquivo inteiro até a última linha começando com Apple
. Na &
string de substituição, insira toda a correspondência e anexe seu texto.
Uma solução portátil que funcionasse sed
também em outras versões seria
sed -e ':a' -e 'N;$! ba' -e 's/.*\(\n\)Apple/&\1Your appended line/'
Aqui o script é dividido nos rótulos e em vez de usar \n
na string de substituição (que não é garantido que funcione com s não-GNU sed
) nos referimos àquele do padrão cercado \(\)
pela \1
referência.
Responder2
Eu vi esse exemploaqui
sed -i -e "\$aTEXTTOEND" <filename>
Informação:
i: edit files in place
e: add the script to the commands to be executed
$: sed address location
a: append command
TEXTTOEND: text to append to end of file
Responder3
Presumo que sua entrada de exemplo seja um arquivo chamado fruits.txt
.
Se você deseja inserir uma linha após a última ocorrência de “Apple”, isso pode ser feito sed
assim:
sed -rz 's/(^(.*\n)?Apple)(\n|$)/\1\ninserted line\n/'
A -r
opção permite expressões regulares estendidas.
A -z
opção diz sed
para usar caracteres NUL (código ASCII 0) como separadores de linha em vez dos caracteres normais de nova linha. Dessa forma, todo o arquivo de texto será processado de uma só vez, em vez de linha por linha.
O sed
comando s/PATTERN/REPLACEMENT/
procura a -r
expressão regular (estendida, por causa de) PATTERN
e a substitui pela REPLACEMENT
string avaliada.
A expressão regular (^(.*\n)?Apple)(\n|$)
corresponde a qualquer número de linhas desde o início ( ^
) até a última ocorrência Apple
seguida por uma quebra de linha ( \n
) ou o final do arquivo ( $
).
Agora toda esta partida é substituída por \1\ninserted line\n
, onde \1
representa o conteúdo original do primeiro grupo de partidas, ou seja, tudo até Apple
. Então, na verdade, ele é inserido inserted line
precedido e seguido por uma quebra de linha ( \n
) logo após a última ocorrência de "Apple".
Isso também funciona se a linha “Apple” for a primeira, a última ou a única linha do arquivo.
Arquivo de entrada de exemplo (adicionada uma linha ao seu exemplo, para deixar o comportamento claro):
Apple
Banana
Apple
Orange
Apple
Cherry
Exemplo de saída:
Apple
Banana
Apple
Orange
Apple
inserted line
Cherry
Se você estiver satisfeito com o resultado e quiser editar fruits.txt
no local, em vez de apenas gerar a modificação, você pode adicionar a -i
opção:
sed -irz 's/(^(.*\n)?Apple)(\n|$)/\1\ninserted line\n/'
Se você deseja apenas anexar uma linha de texto ao final do arquivo, essa sed
não é a ferramenta ideal.
Você pode simplesmente usar o redirecionamento de saída anexado do Bash >>
:
echo "I am new string replaced at last apple" >> fruits.txt
O >>
irá pegar a saída padrão do comando à sua esquerda (o echo
comando aqui simplesmente imprime seus argumentos na saída padrão) e anexa-o ao arquivo especificado à direita. Se o arquivo ainda não existir, ele será criado.
Responder4
Nesta resposta:
- sed+tac (reversão de entrada dupla)
- AWK com entrada dupla
- Perl com entrada dupla
- Perl alternativo com reversão de entrada dupla
sed + tac
Esta abordagem usa 3 processos, primeiro revertendo o arquivo, usando sed para encontrar a primeira correspondência e inserindo o texto desejado após a primeira correspondência, e revertendo o texto novamente.
$ tac input.txt | sed '0,/Apple/{s/Apple/NEW THING\n&/}' | tac
Apple
Banana
Apple
Orange
Apple
NEW THING
something else
something other entirely
blah
A ideia básica é a mesma da versão alternativa do Perl escrita abaixo: a primeira correspondência na lista reversa de linhas é a última correspondência na lista original. A vantagem desta abordagem é a simplicidade e a legibilidade para o usuário.
Essa abordagem funcionará bem para arquivos pequenos, mas será complicada para arquivos grandes e provavelmente levará um tempo considerável para processar uma grande quantidade de linhas.
AWK
Awk pode ser um pouco mais amigável para o que você está tentando fazer:
$ awk -v n="AFTER LAST APPLE" 'NR==FNR&&/Apple/{l=NR};NR!=FNR{if(FNR==l){print $0;print n}else print}' input.txt input.t>
Apple
Banana
Apple
Orange
Apple
AFTER LAST APPLE
something else
something other entirely
blah
A idéia básica aqui é fornecer seu arquivo de entrada para awk duas vezes - primeiro para descobrir onde está a última Apple e, segundo, para inserir o texto quando passarmos a posição da última "maçã" que encontramos na primeira iteração.
A chave para fazer este trabalho é avaliar NR
(que continua contando todas as linhas processadas, totais) e FNR
(número da linha no arquivo atual) variáveis. Quando esses dois arquivos são desiguais, sabemos que estamos processando o segundo arquivo, portanto sabemos que fizemos nossa primeira tentativa de encontrar a localização da última ocorrência de Apple.
Como estamos lendo o arquivo duas vezes, o desempenho dependerá do tamanho do arquivo, mas é melhor que a combinação sed + tac, pois não precisamos reverter o arquivo duas vezes.
Perl
perl -sne '$t+=1;$l=$. if /Apple/ and $.==$t;
if($t!=$.){if($.==$l){print $_ . $n . "\n";}else{print $_}};
close ARGV if eof;' -- -n="AFTER LAST APPLE" input.txt input.txt
A solução Perl segue a mesma ideia da AWK. Passamos a entrada duas vezes e usamos a primeira vez que o arquivo é passado para descobrir onde está a última ocorrência de Apple e armazenar seu número de linha na $l
variável. O que é diferente do awk aqui é que não há FNR
variável em perl, portanto temos que usar $t+=1
e close ARGV if eof
para esse propósito.
O que também é diferente aqui é que com -s
switch perl permite passar argumentos. Neste caso a string que queremos inserir é passada via -n
switch.
Perl alternativo
Aqui está outra maneira de fazer isso em Perl. A ideia é simplesmente ler o arquivo em um array de linhas e reverter esse array. A primeira correspondência na matriz invertida é a última na lista original de linhas. Assim, podemos inserir um texto desejado antes dessa linha, e inverter novamente a lista:
$ perl -le '@a1=<>;END{do{push @a2,"NEW LINE\n" and $f=1 if $_ =~ /Apple/ and !$f; push @a2,$_} for reverse(@a1); print reverse(@a2)}' input.txt
Apple
Banana
Apple
Orange
Apple
NEW LINE
something else
something other entirely
blah