Como remover a última linha de todos os arquivos de um diretório?

17

Eu tenho muitos arquivos de texto em um diretório e desejo remover a última linha de todos os arquivos no diretório.

Como posso fazer isso?

    
por ThehalfarisedPheonix 31.01.2017 / 12:03

6 respostas

4

Se você tiver acesso a vim , poderá usar:

for file in ./*
do
  if [ -f "${file}" ]
  then
    vim -c '$d' -c "wq" "${file}"
  fi
done
    
por 31.01.2017 / 22:32
15

Você pode usar esse bom oneliner se tiver o GNU sed .

 sed -i '$ d' ./*

Ele removerá a última linha de cada arquivo não oculto no diretório atual. Mude -i para GNU sed significa operar no local e '$ d' comanda o sed para excluir a última linha ( $ significa última e d significa exclusão).

    
por 31.01.2017 / 12:10
11

As outras respostas têm problemas se o diretório contiver algo diferente de um arquivo normal ou um arquivo com espaços / novas linhas no nome do arquivo. Aqui está algo que funciona independentemente:

find "$dir" -type f -exec sed -i '$d' '{}' '+'
  • find "$dir" -type f : encontre os arquivos no diretório $dir
    • -type f que são arquivos regulares;
    • -exec executa o comando em cada arquivo encontrado
    • sed -i : edite os arquivos no lugar;
    • '$d' : delete ( d ) a última linha ( $ ).
    • '+' : diz ao find para continuar adicionando argumentos para sed (um pouco mais eficiente do que executar o comando para cada arquivo separadamente, graças a @zwol).

Se você não quiser descer em subdiretórios, poderá adicionar o argumento -maxdepth 1 a find .

    
por 31.01.2017 / 16:11
9

Usando o GNU sed -i '$d' significa ler o arquivo completo e fazer uma cópia dele sem a última linha, enquanto seria muito mais eficiente apenas truncar o arquivo no lugar (pelo menos para arquivos grandes).

Com o% GNUtruncate, você pode fazer:

for file in ./*; do
  [ -f "$file" ] &&
    length=$(tail -n 1 "$file" | wc -c) &&
    [ "$length" -gt 0 ] &&
    truncate -s "-$length" "$file"
done

Se os arquivos forem relativamente pequenos, provavelmente seriam menos eficientes, já que executam vários comandos por arquivo.

Observe que, para arquivos que contêm bytes extras após o último caractere de nova linha (após a última linha) ou em outras palavras, se eles tiverem uma linha não delimitada, dependendo do tail implementação, tail -n 1 retornará apenas os bytes extras (como GNU tail ), ou a última linha (devidamente delimitada) e os bytes extras.

    
por 31.01.2017 / 18:32
6

Uma abordagem mais portátil:

for f in ./*
do
test -f "$f" && ed -s "$f" <<\IN
d
w
q
IN
done

Eu não acho que isso precise de qualquer explicação ... exceto talvez que, nesse caso, d seja o mesmo que $d , já que ed , por padrão, seleciona a última linha.
Isso não pesquisará recursivamente e não processará arquivos ocultos (também conhecidos como dotfiles).
Se você quiser editá-los, consulte Como combinar * com arquivos ocultos dentro de um diretório

    
por 31.01.2017 / 18:33
3

One-liner compatível com POSIX para todos os arquivos começando recursivamente no diretório atual, incluindo arquivos de pontos:

find . -type f -exec sh -c 'for f; do printf "\$d\nx\n" | ex "$f"; done' sh {} +

Apenas para .txt , não recursivamente:

find . -path '*/*/*' -prune -o -type f -name '*.txt' -exec sh -c 'for f; do printf "\$d\nx\n" | ex "$f"; done' sh {} +

Veja também:

por 01.02.2017 / 05:01