Como posso editar as últimas n linhas em um arquivo?

10

Existe um comando que me permita editar as últimas n linhas em um arquivo? Eu tenho vários arquivos, todos com um número diferente de linhas dentro. Mas eu gostaria de modificar as últimas n linhas em cada arquivo. O objetivo é substituir vírgulas por ponto e vírgula nas últimas n linhas. Mas apenas nas últimas n linhas.

Não quero excluir nenhuma linha, só quero substituir cada vírgula por um ponto-e-vírgula nas últimas n linhas em cada arquivo.

Usando o comando sed, sou capaz de substituir a última linha por este comando. Conforme descrito aqui: Como remover texto na última linha de um arquivo?

Mas isso só me permite modificar a última linha, e não o último n número de linhas.

sku2003
fonte
1
Simplesmente usando sed '24,$s/,/:/g' filename onde 24é o line` começando
Valentin Bajrami

Respostas:

13

Para substituir vírgulas por ponto e vírgula nas últimas n linhas por ed:

n=3
ed -s input <<< '$-'$((n-1))$',$s/,/;/g\nwq'

Dividindo isso:

  • ed -s = executado silenciosamente (não relate os bytes escritos no final)
  • '$-'= do final do arquivo ( $) menos ...
  • $((n-1)) = n-1 linhas ...
  • ( $' ... '= cite o restante do comando para protegê-lo do shell)
  • ,$s/,/;/g= ... até o final do arquivo ( ,$), pesquise e substitua todas as vírgulas por ponto e vírgula.
  • \nwq = finalize o comando anterior e salve e saia

Para substituir vírgulas por ponto e vírgula nas últimas n linhas por sed:

n=3
sed -i "$(( $(wc -l < input) - n + 1)),\$s/,/;/g" input

Desmembrando isso:

  • -i = editar o arquivo "no local"
  • $(( ... )) = faça algumas contas:
  • $( wc -l < input) = obtém o número de linhas no arquivo
  • -n + 1 = retrocede n-1 linhas
  • ,\$ = de n-1 linhas até o final do arquivo:
  • s/,/;/g = substitua as vírgulas por ponto e vírgula.
Jeff Schaller
fonte
Pode substituir wc -l < inputpor wc -l input. Deve ser mais rápido por alguns nanossegundos :)
gardenhead
1
exceto que wc -l inputgera o nome do arquivo também; nós só queremos a contagem de linha
Jeff Schaller
O primeiro conselho com ed é bom e simples.
precisa saber é o seguinte
Na verdade, acabei recebendo ajuda de um lado completamente diferente. Isso resultou nessa parte estranha do código, que faz o truque, mas é longa e estranha. Eu acho que essas são algumas das coisas estranhas com as quais você pode encontrar uma solução. De qualquer forma o seu é mais bonito, mais curto e menos complicada do que a minha própria: O código aqui também funcionou:
sku2003
cat input.file | sed 's /, /, \ n / g' | sed -n '1! G; h; $ p' | awk -vn = 2 'NR <= n {gsub (",", ";", $ 0)} {print}' | sed -n '1! G; h; $ p' | sed '/ ^ \ s * $ / d'> output.file
sku2003
6

Solução usando tac e sed para substituir cada vírgula por ponto e vírgula nas últimas 50 linhas de file.txt:

tac file.txt | sed '1,50s/,/;/g' | tac
Gohu
fonte
5

Com o GNU heade um shell semelhante ao Bourne:

n=20
{ head -n -"$n"; tr , ';'; } < file 1<> file

Estamos substituindo o arquivo por si próprio. Isso é OK aqui para uma transliteração byte-to-byte, mas que não necessariamente se a modificação implica a alteração do tamanho do arquivo (nesse caso, você gostaria de substituir 1<> filecom > other-file && mv other-file file, por exemplo).

Stéphane Chazelas
fonte
1
Aqui estão os documentos para os1<> quais eu não estava familiarizado. Além disso, a spongepartir do moreutils é uma boa ferramenta para pipelines nos quais você deseja substituir sua entrada.
Miles
1

Vamos supor que queremos substituir as últimas 7linhas da seguinte sequência pelo shell script e pela implementação GNU de sed:

$ seq 20
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

Etapa 1 : permite obter o número da última linha da sequência, como a seguir. Dê uma olhada nisso e naquilo :

$ lastLine=`seq 20|sed -n '$='`

$ echo $lastLine 
20

Etapa 2 : vamos definir o número de linhas (no final da sequência) que pretendemos editar:

$ numberOfLines=7

$ echo $numberOfLines 
7

Etapa 3 : vamos calcular a linha de partida com base nas variáveis ​​anteriores, como a seguir. Dê uma olhada nisso :

$ startLine=`expr $lastLine - $numberOfLines + 1`

$ echo $startLine 
14

Etapa 4 : agora, podemos substituir as últimas 7 linhas de sequência por outra coisa, como a seguir. Dê uma olhada nisso :

$ seq 20|sed -e "$startLine,+$numberOfLines{s/[12]/WoW/}"
1
2
3
4
5
6
7
8
9
10
11
12
13
WoW4
WoW5
WoW6
WoW7
WoW8
WoW9
WoW0

A etapa 4 está usando a seção 4.4 da página do manual sed, que diz:

'ADDR1,+N'
     Matches ADDR1 and the N lines following ADDR1.

Etapa 4, também está usando aspas duplas, conforme mencionado aqui .


Bem, as 4 etapas são desnecessárias se usarmos a resposta de Gohu assim:

$ seq 20 |tac|sed -e '1,7{s/[12]/WoW/}'|tac
1
2
3
4
5
6
7
8
9
10
11
12
13
WoW4
WoW5
WoW6
WoW7
WoW8
WoW9
WoW0
user3405291
fonte
0

Use taile cante para sed:

tail -n 20 file | sed 's/,/;/g'

Isso funciona no arquivo nas últimas 20 linhas. Se você deseja que você mude diretamente para o arquivo, use:

tail -n 20 file | sed -i 's/,/;/g'

John Pateta
fonte