Como usar o sed para remover as últimas n linhas de um arquivo


148

Eu quero remover algumas linhas n do final de um arquivo. Isso pode ser feito usando sed?

Por exemplo, para remover linhas de 2 a 4, posso usar

$ sed '2,4d' file

Mas eu não sei os números das linhas. Eu posso excluir a última linha usando

$sed $d file

mas quero saber como remover n linhas do final. Por favor, deixe-me saber como fazer isso usando o sed ou algum outro método.


2
@arashkordi: que usa uma contagem de linhas na parte superior do arquivo, não na parte inferior.
AMS

Pergunta relacionada sobre superusuários .
Thor

//, Talvez considere reformular a questão para torná-la mais geral do que justa sed ?
Nathan Basanese

1
sed $d fileretorna um erro. Em vez disso, $ d deve estar entre aspas, desta forma:sed '$d' file
Akronix 11/17/17

Respostas:


221

Não sei sed, mas isso pode ser feito com head:

head -n -2 myfile.txt

19
+1 por simplicidade. O comando sed equivalente é feio: sed -e :a -e '$d;N;2,5ba' -e 'P;D' file( ,5nas últimas 5 linhas).
Marc B

62
Observe que isso funciona com algumas versões head, mas não é padrão. De fato, o padrão para os headestados:The application shall ensure that the number option-argument is a positive decimal integer.
William Pursell

21
Para adicionar à resposta de @ WilliamPursell ... No shell padrão do Mac OS, isso não funciona.
JDS

7
Nem no BSD, mas a questão está marcada com Linux.
AMS

3
menos 1, porque, além de OSX, este não está trabalhando para mim em Ubuntu14 -head: illegal line count -- -2
Michael Durrant

30

Se a opção codificação n for uma opção, você poderá usar chamadas seqüenciais para sed. Por exemplo, para excluir as últimas três linhas, exclua a última linha três vezes:

sed '$d' file | sed '$d' | sed '$d'

2
Isso, mais -i para editar o arquivo no lugar, em vez de despejá-lo no stdout, funcionou para mim.
WAF

27

Dos sed one-liners :

# delete the last 10 lines of a file
sed -e :a -e '$d;N;2,10ba' -e 'P;D'   # method 1
sed -n -e :a -e '1,10!{P;N;D;};N;ba'  # method 2

Parece ser o que você está procurando.


@Thor, você pode alterar quantas linhas são removidas do arquivo, alterando o arquivo 10in'$d;N;2,10ba'
Alexej Magura

1
@AlexejMagura: Eu estava comentando uma versão anterior da resposta.
Thor

22

Um engraçado e simples sede tacsolução:

n=4
tac file.txt | sed "1,$n{d}" | tac

NOTA

  • aspas duplas "são necessárias para o shell avaliar a $nvariável no sedcomando. Entre aspas simples, nenhuma interpolação será executada.
  • tacé um catreverso, vejaman 1 tac
  • o {}in sedexiste para separar $n& d(caso contrário, o shell tenta interpolar a $ndvariável inexistente )

7
fyi há tacno OSX
Michael Durrant

1
@MichaelDurrant port info coreutils|| brew info coreutils;
vozes

19

Use sed, mas deixe o shell fazer as contas, com o objetivo de usar o dcomando, fornecendo um intervalo (para remover as últimas 23 linhas):

sed -i "$(($(wc -l < file)-22)),\$d" file

Para remover as últimas 3 linhas, de dentro para fora:

$(wc -l < file)

Dá o número de linhas do arquivo: digamos 2196

Queremos remover as últimas 23 linhas, portanto, para o lado esquerdo ou o intervalo:

$((2196-22))

Dá: 2174 Assim, a interpretação original do sed após o shell é:

sed -i '2174,$d' file

Com -ia edição no local, o arquivo agora é 2173 linhas!

Se você deseja salvá-lo em um novo arquivo, o código é:

sed -i '2174,$d' file > outputfile

15

Você poderia usar a cabeça para isso.

Usar

$ head --lines=-N file > new_file

onde N é o número de linhas que você deseja remover do arquivo.

O conteúdo do arquivo original menos as últimas N linhas agora está em new_file


8

Apenas para completar, gostaria de adicionar minha solução. Acabei fazendo isso com o padrão ed:

ed -s sometextfile <<< $'-2,$d\nwq'

Isso exclui as 2 últimas linhas usando a edição no local (embora não usar um arquivo temporário na /tmp!!)


5

Para truncar arquivos muito grandes realmente no local, temos truncatecomando. Ele não sabe sobre linhas, mas tail+ wcpode converter linhas em bytes:

file=bigone.log
lines=3
truncate -s -$(tail -$lines $file | wc -c) $file

Existe uma condição óbvia de corrida se o arquivo for gravado ao mesmo tempo. Nesse caso, pode ser melhor usá head-lo - ele conta os bytes desde o início do arquivo (mente IO do disco), portanto, sempre truncaremos no limite da linha (possivelmente mais linhas do que o esperado se o arquivo for gravado ativamente):

truncate -s $(head -n -$lines $file | wc -c) $file

Handy one-liner, se você falhar, tente colocar a senha no lugar do nome de usuário:

truncate -s $(head -n -5 /var/log/secure | wc -c) /var/log/secure

4

Isso pode funcionar para você (GNU sed):

sed ':a;$!N;1,4ba;P;$d;D' file

Agradável. Você perdeu o apóstrofo final ( ').
Thor

desculpe pelo comentário tão tarde, mas eu tento e (adaptado para o meu AIX / posix) falhou ao imprimir todos, exceto a última linha. Lendo o código, eu não entendo como as últimas quatro linhas são removidas, o loop explícito está nas 4 primeiras linhas, então certamente loop após a d, Dou Pcom GNU sed e não na versão posix. Parece que as linhas não são impressas após o De permanecem no buffer de trabalho para um novo loop sem passar para o 'final' da linha de ações.
NeronLeVelu

@NeronLeVelu o programa lê em uma janela de 4 linhas no espaço do padrão e, em seguida, anexa a próxima linha e imprime a primeira até chegar ao final do arquivo, onde exclui as linhas restantes.
Potong

@potong sim, eu testei em um GNU sed e ele mantém o buffer entre as linhas em que o posix o descarrega após o Defeito oposto.
NeronLeVelu

4

A maioria das respostas acima parece exigir comandos / extensões do GNU:

    $ head -n -2 myfile.txt
    -2: Badly formed number

Para uma solução um pouco mais portável:

     perl -ne 'push(@fifo,$_);print shift(@fifo) if @fifo > 10;'

OU

     perl -ne 'push(@buf,$_);END{print @buf[0 ... $#buf-10]}'

OU

     awk '{buf[NR-1]=$0;}END{ for ( i=0; i < (NR-10); i++){ print buf[i];} }'

Onde "10" é "n".


2

Com as respostas aqui, você já deve ter aprendido que o sed não é a melhor ferramenta para esta aplicação.

No entanto, acho que existe uma maneira de fazer isso usando o sed; a idéia é acrescentar N linhas para manter o espaço até que você consiga ler sem pressionar EOF. Quando o EOF for atingido, imprima o conteúdo do espaço em espera e saia.

sed -e '$!{N;N;N;N;N;N;H;}' -e x

O comando sed acima omitirá as últimas 5 linhas.


2

Tente o seguinte comando:

n = line number
tail -r file_name | sed '1,nd' | tail -r

Por favor, explique como isso funciona. FYI - para inicializar variáveis ​​de shell, não devemos ter um espaço ao redor =.
codeforester

@codeforester A primeira linha foi um comentário, eu acho. Ele apenas usa tail -ronde outros foram usados tacpara inverter a ordem das linhas e fazer com que as últimas nsejam as nprimeiras.
Nicolas Melay

2

Isso pode ser feito em 3 etapas:

a) Conte o número de linhas no arquivo que você deseja editar:

 n=`cat myfile |wc -l`

b) Subtraia desse número o número de linhas a serem excluídas:

 x=$((n-3))

c) Diga ao sed para excluir desse número de linha ( $x) até o final:

 sed "$x,\$d" myfile

Matemática ruim. Se você precisar excluir 3 linhas, subtraia 3, mas ADICIONAR 1. Com sua matemática, se o arquivo tiver 10 linhas, 10 - 3 = 7 e você estiver usando sedpara excluir as linhas 7 a 10, ou seja, 4 linhas, não 3.
mathguy

1

Você pode obter a contagem total de linhas wc -l <file>e usar

head -n <total lines - lines to remove> <file>


1

Isso removerá as últimas 3 linhas de file:

for i in $(seq 1 3); do sed -i '$d' file; done;


1
Isso é muito caro para arquivos grandes - o arquivo inteiro precisa ser lido e reescrito n vezes! E quantos garfos para sed.
codeforester

embora essa possa não ser a solução mais eficiente, é a mais direta e fácil de entender
Dharam

0

Eu prefiro esta solução;

head -$(gcalctool -s $(cat file | wc -l)-N) file

onde N é o número de linhas a serem removidas.


0
sed -n ':pre
1,4 {N;b pre
    }
:cycle
$!{P;N;D;b cycle
  }' YourFile

versão posix


0

Para excluir as últimas 4 linhas:

$ nl -b a file | sort -k1,1nr | sed '1, 4 d' | sort -k1,1n | sed 's/^ *[0-9]*\t//'   

um pouco pesado (especialmente em recursos) se compara a uma cabeça. Pelo menos tac file | sed '1,4d' | tacporque a classificação remove o prefixo e muitos recursos.
NeronLeVelu

@NeronLeVelu você está certo, mas não há nenhum taccomando é alguns sistemas (por exemplo FreeBSD?)
mstafreshi

0

Eu vim com isso, onde n é o número de linhas que você deseja excluir:

count=`wc -l file`
lines=`expr "$count" - n`
head -n "$lines" file > temp.txt
mv temp.txt file
rm -f temp.txt

É uma pequena rotatória, mas acho fácil de seguir.

  1. Contar o número de linhas no arquivo principal
  2. Subtraia o número de linhas que você deseja remover da contagem
  3. Imprima o número de linhas que deseja manter e armazene em um arquivo temporário
  4. Substitua o arquivo principal pelo arquivo temporário
  5. Remova o arquivo temporário

0

Para excluir as últimas N linhas de um arquivo, você pode usar o mesmo conceito de

$ sed '2,4d' file

Você pode usar um comando combo com tail para reverter o arquivo: se N for 5

$ tail -r file | sed '1,5d' file | tail -r > file

E dessa maneira também é executado onde o head -n -5 filecomando não é executado (como em um mac!).


-2

Isso removerá as últimas 12 linhas

sed -n -e :a -e '1,10!{P;N;D;};N;ba'

1
Um pouco de explicação ajudaria por favor.
Richard Wiseman
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.