Remova a última linha de um arquivo no Bash

332

Eu tenho um arquivo foo.txt, contendo as seguintes linhas:

a
b
c

Eu quero um comando simples que resulte no conteúdo de foo.txtser:

a
b
Fragsworth
fonte

Respostas:

408

Usando GNU sed:

sed -i '$ d' foo.txt

A -iopção não existe em GNU sedversões anteriores à 3.95, portanto, você deve usá-la como um filtro com um arquivo temporário:

cp foo.txt foo.txt.tmp
sed '$ d' foo.txt.tmp > foo.txt
rm -f foo.txt.tmp

Claro, nesse caso, você também pode usar em head -n -1vez de sed.

Mac OS:

No Mac OS X (a partir de 10.7.4), o equivalente ao sed -icomando acima é

sed -i '' -e '$ d' foo.txt
thkala
fonte
56
No Mac OS X (a partir de 10.7.4), o equivalente ao sed -icomando acima é sed -i '' -e '$ d' foo.txt. Além disso, head -n -1atualmente não funcionará em um Mac.
usar o seguinte código
26
Você poderia explicar o que '$ d' regex faz? É uma questão de remover a última linha, então acho que essa é a parte mais importante para todos que visualizam essa pergunta. Obrigado :)
kravemir
38
@Miro: De modo algum é $ duma regex. É um comando sed. dé o comando para excluir uma linha, enquanto $significa "a última linha no arquivo". Ao especificar um local (chamado "range" no sed lingo) antes de um comando, esse comando é aplicado apenas ao local especificado. Portanto, este comando diz explicitamente "no intervalo da última linha de um arquivo, exclua-o". Muito liso e direto ao ponto, se você me perguntar.
Daniel Kamil Kozar 4/14
1
Como você remove a última linha, mas apenas se for uma linha vazia?
skan
1
@ Alex: atualizado para o GNU sed; nenhuma idéia sobre os vários BSD / Unix / MacOS sed versões ...
thkala
279

Essa é de longe a solução mais rápida e simples, especialmente em arquivos grandes:

head -n -1 foo.txt > temp.txt ; mv temp.txt foo.txt

se você deseja excluir a linha superior, use o seguinte:

tail -n +2 foo.txt

o que significa linhas de saída começando na linha 2.

Não use sedpara excluir linhas da parte superior ou inferior de um arquivo - é muito muito lento se o arquivo for grande.

John
fonte
16
head -n -1 foo.txté suficiente
ДМИТРИЙ МАЛИКОВ
20
head -n -1 não funciona na cabeça do bdsutils. pelo menos não na versão que o macos está usando, então isso não funciona.
Johannes_lalala
23
@johannes_lalala No Mac, você pode brew install coreutils(utilitários principais do GNU) e usar em gheadvez de head.
interestinglythere
Aqui está um script simples do bash que o automatiza caso você tenha vários arquivos com diferentes números de linhas na parte inferior para excluir: cat TAILfixer FILE=$1; TAIL=$2; head -n -${TAIL} ${FILE} > temp ; mv temp ${FILE}Execute-o para excluir 4 linhas, por exemplo, do myfile como: é ./TAILfixer myfile 4claro que primeiro o torna executávelchmod +x TAILfixer
FatihSarigol
Polegares para cima porque funcionou, mas, apesar de toda a comparação sed, esse comando também é muito lento
Jeff Diederiks
121

Eu tive problemas com todas as respostas aqui porque estava trabalhando com um arquivo ENORME (~ 300 GB) e nenhuma das soluções foi dimensionada. Aqui está a minha solução:

dd if=/dev/null of=<filename> bs=1 seek=$(echo $(stat --format=%s <filename> ) - $( tail -n1 <filename> | wc -c) | bc )

Em palavras: Descubra o tamanho do arquivo que você quer acabar com (comprimento de arquivo menos o comprimento da duração da sua última linha, usando bc) e, conjunto que posição para ser o fim do arquivo (por dding um byte de /dev/nullem isto).

Isso é rápido porque tailcomeça a ler a partir do final e ddsubstitui o arquivo no lugar em vez de copiar (e analisar) todas as linhas do arquivo, que é o que as outras soluções fazem.

NOTA: Isso remove a linha do arquivo no lugar! Faça um backup ou teste em um arquivo fictício antes de testá-lo em seu próprio arquivo!

Yossi Farjoun
fonte
3
Eu nem sabia sobre dd. Essa deve ser a resposta principal. Muito obrigado! É uma pena que a solução não funcione para (bloquear) arquivos compactados em gzip.
tommy.carstensen
4
Abordagem muito, muito inteligente! Apenas uma observação: requer e opera em um arquivo real, portanto não pode ser usado em pipes, substituições de comandos, redirecionamentos e cadeias desse tipo.
MestreLion 30/08/2015
3
Essa deve ser a resposta principal. Trabalhei instantaneamente para meu arquivo de ~ 8 GB.
precisa
8
usuários mac: dd if = / dev / null of = <nome do arquivo> bs = 1 seek = $ (eco $ (stat -f =% z <nome do arquivo> | cut -c 2-) - $ (tail -n1 <nome do arquivo> | wc -c) | bc)
Igor
2
Essa abordagem é o caminho a seguir para arquivos grandes!
thomas.han
61

Para remover a última linha de um arquivo sem ler o arquivo inteiro ou reescrever nada , você pode usar

tail -n 1 "$file" | wc -c | xargs -I {} truncate "$file" -s -{}

Para remover a última linha e também imprimi-la no stdout ("pop" it), você pode combinar esse comando com tee:

tail -n 1 "$file" | tee >(wc -c | xargs -I {} truncate "$file" -s -{})

Esses comandos podem processar eficientemente um arquivo muito grande. É semelhante e inspirado pela resposta de Yossi, mas evita o uso de algumas funções extras.

Se você vai usá-las repetidamente e desejar manipulação de erros e outros recursos, use o poptailcomando aqui: https://github.com/donm/evenmoreutils

oh apesar
fonte
3
Nota rápida: truncatenão está disponível no OS X por padrão. Mas é fácil de instalar usando Brew: brew install truncate.
Guillaume Boudreau
3
Muito agradável. Muito melhor sem dd. Eu morria de medo de usar dd como um único dígito extraviado ou carta pode significar um desastre .. +1
Yossi Farjoun
1
(AVISO DE Piadas) Na verdade, ("dd" -> "desastre") requer 1 substituição e 6 inserções; dificilmente "um único dígito ou letra extraviada". :)
Kyle
29

Usuários de Mac

Se você deseja excluir apenas a última linha sem alterar o arquivo, faça

sed -e '$ d' foo.txt

Se você deseja excluir a última linha do arquivo de entrada, faça

sed -i '' -e '$ d' foo.txt

manpreet singh
fonte
Isso funciona para mim, mas eu não entendo. de qualquer forma, funcionou.
Melvin
O sinalizador -i ''diz ao sed para modificar o arquivo no local, mantendo um backup em um arquivo com a extensão fornecida como parâmetro. Como o parâmetro é uma sequência vazia, nenhum arquivo de backup é criado. -ediz ao sed para executar um comando. O comando $ dsignifica: encontre a última linha ( $) e exclua-a ( d).
Krzysztof Szafranek 3/03/19
24

Para usuários de Mac:

No Mac, a cabeça -n -1 não funcionará. E, eu estava tentando encontrar uma solução simples [sem se preocupar com o tempo de processamento] para resolver esse problema usando apenas os comandos "head" e / ou "tail".

Tentei a seguinte sequência de comandos e fiquei feliz por poder resolvê-la usando o comando "tail" [com as opções disponíveis no Mac]. Portanto, se você estiver no Mac e quiser usar apenas "cauda" para resolver esse problema, poderá usar este comando:

arquivo cat.txt | cauda -r | cauda -n +2 | cauda -r

Explicação:

1> tail -r: simplesmente inverte a ordem das linhas em sua entrada

2> tail -n +2: imprime todas as linhas a partir da segunda linha em sua entrada

Sarfraaz Ahmed
fonte
2
A instalação de coreutils usando o Homebrew fornecerá a cabeça do GNU como 'ghead', permitindo que você façaghead -n -1
um pouco fora de
13
echo -e '$d\nw\nq'| ed foo.txt
lhf
fonte
2
Para uma solução de filtro que não edita o arquivo no local, use sed '$d'.
lhf 3/02/11
8
awk 'NR>1{print buf}{buf = $0}'

Essencialmente, este código diz o seguinte:

Para cada linha após a primeira, imprima a linha em buffer

para cada linha, redefina o buffer

O buffer está atrasado em uma linha, portanto, você acaba imprimindo as linhas 1 a n-1

Foo Bah
fonte
2
Esta solução é um filtro e não edita o arquivo no local.
lhf 3/02/11
0
awk "NR != `wc -l < text.file`" text.file |> text.file

Este trecho faz o truque.

Michael Kingski
fonte
Isso apenas apaga o arquivo inteiro para mim.
Kartik Chugh
0

Ambas as soluções estão aqui de outras formas. Achei isso um pouco mais prático, claro e útil:

Usando dd:

BADLINESCOUNT=1
ORIGINALFILE=/tmp/whatever
dd if=${ORIGINALFILE} of=${ORIGINALFILE}.tmp status=none bs=1 count=$(printf "$(stat --format=%s ${ORIGINALFILE}) - $(tail -n${BADLINESCOUNT} ${ORIGINALFILE} | wc -c)\n" | bc )
/bin/mv -f ${ORIGINALFILE}.tmp ${ORIGINALFILE}

Usando truncado:

BADLINESCOUNT=1
ORIGINALFILE=/tmp/whatever
truncate -s $(printf "$(stat --format=%s ${ORIGINALFILE}) - $(tail -n${BADLINESCOUNT} ${ORIGINALFILE} | wc -c)\n" | bc ) ${ORIGINALFILE}
luz virtual
fonte
Ai. Muito complicado.
Robert
0

Linux

$ é a última linha, d para exclusão:

sed '$d' ~/path/to/your/file/name

Mac OS

Equivalente ao sed -i

sed -i '' -e '$ d' ~/path/to/your/file/name
jasonleonhard
fonte
0

Veja como você pode fazer isso manualmente (eu pessoalmente uso muito esse método quando preciso remover rapidamente a última linha de um arquivo):

vim + [FILE]

Este + sinal indica que, quando o arquivo é aberto no editor de texto vim, o cursor será posicionado na última linha do arquivo.

Agora basta pressionar dduas vezes no teclado. Isso fará exatamente o que você deseja - remova a última linha. Depois disso, pressione :seguido de xe, em seguida, pressione Enter. Isso salvará as alterações e o levará de volta ao shell. Agora, a última linha foi removida com sucesso.

Michael Rybkin
fonte
2
A ênfase na simplicidade foi perdida aqui #
Peter M
-4

Ruby (1.9+)

ruby -ne 'BEGIN{prv=""};print prv ; prv=$_;' file
Kurumi
fonte