Como encontrar arquivos que não possuem linha vazia no final?

9

Eu tenho arquivos em subdiretórios do diretório atual que podem ou não ter novas linhas no final; Como posso encontrar arquivos que não tenham uma nova linha no final?

Eu tentei isso:

find . -name '*.styl' | while read file; do
    awk 'END{print}' $file | grep -E '^$' > /dev/null || echo $file;
done

mas não funciona. awk 'END{print}' $file imprime a linha antes de uma nova linha vazia, igual a tail -n 1 $file .

    
por jcubic 12.10.2016 / 15:39

4 respostas

13

Para esclarecer, o caractere LF (também conhecido como \n ou newline) é a linha delimitador , não é o separador de linha. Uma linha não está terminada, a menos que seja terminada por um caractere de nova linha. Um arquivo que contém apenas a\nb não é um arquivo de texto válido porque contém caracteres após a última linha. O mesmo para um arquivo que contém apenas a . Um arquivo que contém a\n contém uma linha não vazia.

Assim, um arquivo que termina com pelo menos uma linha vazia termina com dois caracteres de nova linha ou contém um único caractere de nova linha.

Se:

 tail -c 2 file | od -An -vtc

Gera \n ou \n \n , então o arquivo contém pelo menos uma linha vazia à direita. Se não produzir nada, então esse é um arquivo vazio, se gerar <anything-but-od> \n , então terminará em uma linha não vazia. Qualquer outra coisa, não é um arquivo de texto.

Agora, para usar isso para localizar arquivos que terminam em uma linha vazia, é eficiente (especialmente para arquivos grandes), pois lê apenas os últimos dois bytes dos arquivos, mas primeiro a saída não é facilmente analisável programaticamente, especialmente considerando que não é consistente de uma implementação de tail para a próxima, e precisamos executar um od e um zsh por arquivo.

find . -type f -size +0 -exec gawk '
  ENDFILE{if ($0 == "") print FILENAME}' {} +

(para encontrar arquivos terminados em uma linha vazia) executaria o mínimo de comandos possível, mas significaria ler o conteúdo completo de todos os arquivos.

Idealmente, você precisaria de um shell que pudesse ler o final de um arquivo por si só.

com %code% :

zmodload zsh/system
for f (**/*(D.L+0)) {
  {
    sysseek -w end -2
    sysread
    [[ $REPLY = $'\n' || $REPLY = $'\n\n' ]] && print -r -- $f
  } < $f
}
    
por 12.10.2016 / 17:48
6

Com gnu sed e um shell como zsh (ou bash com shopt -s globstar ):

sed -ns '${/./F}' ./**/*.styl

isso verifica se a última linha de cada arquivo não está vazia, se assim for, ele imprime o nome do arquivo.
Se você quiser o oposto (nomes de arquivos de impressão, se a última linha estiver vazia), basta substituir /./ por /^$/

    
por 12.10.2016 / 16:16
4

Um arquivo de texto terminado corretamente com uma última linha vazia termina em dois \n .

Então, esperamos que tail -c2 seja igual a $'\n\n' .

Infelizmente, expansões de comando removem novas linhas à direita. Vamos precisar de um pouco de ajustes.

f=filename
nl='
'
t=$(tail -c2 $f; printf x)  # capture the last two characters.
r="${nl}${nl}$"                 # regex for: "ends in two newlines".
[[ ${t%x} =~ $r ]] &&  echo "file $f ends in an empty line"

Poderíamos até expandir um pouco para verificar quais arquivos não possuem uma nova linha:

nl='
'
nl=$'\n'
find . -type f -name '*.styl' | while read f; do
    t=$(tail -c2 $f; printf x); r1="${nl}$"; r2="${nl}${r1}"
    [[ ${t%x} =~ $r1 ]] || echo "file $f is missing a trailing newline"
    [[ ${t%x} =~ $r2 ]] && echo "$f"
done

Observe que a nova linha pode ser alterada para algo como $'\r\n , se necessário.
Nesse caso, altere também tail -c2 para tail -c4 .

    
por 13.11.2016 / 00:42
0
for file in *; do
    # Check if the file is readable to avoid clutter
    if cat "./$file" 2&>1 /dev/null; then
        # Compare the last character with a single newline character.
        if [ -n "$(tail -c 1 -- "./$file")" ]; then
            echo "$file"
        fi
        # Also report empty files.
        if [ $(wc -c  < "./$file") -eq 0 ]; then
            echo "$file"
        fi
    fi
done
    
por 12.10.2016 / 15:56