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