Eu vi este exemplo aqui
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
Exemplo:
Entrada:
Apple
Banana
Apple
Orange
Apple
Saída:
Apple
Banana
Apple
Orange
Apple
I am new string replaced at last apple
Eu vi este exemplo aqui
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
Com o GNU sed
, você pode fazer isso:
sed ':a;N;$! ba;s/.*\nApple/&\nYour appended line/'
O padrão :a;N;$! ba
acrescenta linhas do arquivo ao buffer com N
, enquanto o final do arquivo ( $
endereço) não é atingido ( !
) (pule para :a
com ba
). Portanto, se você sair do loop, todas as linhas estarão no buffer e o padrão de procura .*\nApple
corresponderá ao arquivo inteiro até a última linha, começando com Apple
. O &
na string de substituição insere toda a correspondência e anexa seu texto.
Uma solução portátil trabalhando em outras versões sed
também seria
sed -e ':a' -e 'N;$! ba' -e 's/.*\(\n\)Apple/&Your appended line/'
Aqui, o script é dividido nos rótulos e, em vez de usar \n
na cadeia de substituição (que não é garantida para funcionar com não-GNU sed
s), nos referimos àquele do padrão cercado com \(\)
com a referência .
Suponho que sua entrada de exemplo seja um arquivo chamado fruits.txt
.
Se você quiser inserir uma linha após a última ocorrência de "Apple", isso pode ser feito com sed
da seguinte forma:
sed -rz 's/(^(.*\n)?Apple)(\n|$)/\ninserted line\n/'
A opção -r
permite expressões regulares estendidas.
A opção -z
informa a sed
a 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
command s/PATTERN/REPLACEMENT/
procura pela expressão regular (estendida, devido a -r
) em PATTERN
e a substitui pela cadeia REPLACEMENT
avaliada.
A expressão regular (^(.*\n)?Apple)(\n|$)
corresponde a qualquer número de linhas desde o início ( ^
) até a última ocorrência de Apple
seguido por uma quebra de linha ( \n
) ou o final do arquivo ( $
).
Agora, toda essa correspondência é substituída por \ninserted line\n
, em que corresponde ao conteúdo original do primeiro grupo de correspondências, ou seja, tudo até
Apple
. Então, na verdade, ele insere inserted line
preceded 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, última ou única linha do arquivo.
Exemplo de arquivo de entrada (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ê está feliz com o resultado e quer editar fruits.txt
no lugar, em vez de apenas enviar a modificação, você pode adicionar a opção -i
:
sed -irz 's/(^(.*\n)?Apple)(\n|$)/\ninserted line\n/'
Se você quiser apenas anexar uma linha de texto ao final desse arquivo, sed
não é a ferramenta ideal.
Você pode simplesmente usar o redirecionamento de saída >>
do Bash em seguida:
echo "I am new string replaced at last apple" >> fruits.txt
O >>
terá a saída padrão do comando à sua esquerda (o comando echo
aqui simplesmente imprime seus argumentos para a saída padrão) e o anexa ao arquivo especificado está correto. Se o arquivo ainda não existir, ele será criado.
Nesta resposta:
Essa abordagem usa 3 processos, primeiro invertendo o arquivo, usando sed para encontrar a primeira correspondência e inserir o texto desejado após a primeira correspondência, e invertendo 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 que a versão alternativa do perl escrita abaixo: a primeira correspondência na lista inversa de linhas é a última correspondência na lista original. A vantagem dessa abordagem é a simplicidade e a legibilidade do usuário.
Essa abordagem funcionará bem para arquivos pequenos, mas é trabalhosa para arquivos grandes e provavelmente levará um tempo considerável para processar grandes quantidades de linhas.
O Awk pode ser um pouco amigo do 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 ideia básica aqui é dar o seu arquivo de entrada para o awk duas vezes - primeiro para descobrir onde a última Apple está, e segundo para inserir o texto quando passarmos a posição da última "maçã" que encontramos na primeira iteração. / p>
A chave para fazer isso funcionar é avaliar as variáveis NR
(que continua contando todas as linhas processadas, totais) e FNR
(número da linha no arquivo atual). Quando esses dois arquivos são desiguais, sabemos que estamos processando o segundo arquivo, portanto sabemos que fizemos nossa primeira tentativa de encontrar o local da última ocorrência da Apple.
Como estamos lendo o arquivo duas vezes, o desempenho dependerá do tamanho do arquivo, mas é melhor do que a combinação sed + tac, já que não precisamos reverter o arquivo duas vezes.
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 que a AWK. Passamos a entrada duas vezes, e usamos a primeira vez que o arquivo é passado para encontrar onde é a última ocorrência da Apple e armazenar seu número de linha na variável $l
. 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 que você passe argumentos. Nesse caso, a string que queremos inserir é passada pela opção -n
.
Aqui está outra maneira de fazer isso em Perl. A ideia é simplesmente ler o arquivo no array de linhas e reverter esse array. A primeira correspondência na matriz invertida é a última da lista original de linhas. Assim, podemos inserir um texto desejado antes dessa linha e inverter a lista novamente:
$ 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