Gostaria de inserir uma nova string na última linha com algum texto usando sed, [closed]

2

Exemplo:

Entrada:

Apple
Banana
Apple
Orange
Apple

Saída:

Apple
Banana
Apple
Orange
Apple
I am new string replaced at last apple
    
por user1270846 20.05.2017 / 13:31

4 respostas

3

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
    
por George Udosen 20.05.2017 / 15:18
2

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 .

    
por Philippos 20.05.2017 / 13:52
2

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.

    
por Byte Commander 20.05.2017 / 13:44
2

Nesta resposta:

  1. sed + tac (inversão de entrada dupla)
  2. AWK com entrada dupla
  3. Perl com entrada dupla
  4. Perl alternativa com reversão de entrada dupla

sed + tac

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.

AWK

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

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 .

Alternative Perl

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
    
por Sergiy Kolodyazhnyy 20.05.2017 / 14:43