Come trovare file che non hanno una riga vuota alla fine?


9

Ho dei file nelle sottodirectory della directory corrente che possono avere o meno nuove righe alla fine; come posso trovare file che non hanno una nuova riga alla fine?

Ho provato questo:

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

ma non funziona. awk 'END{print}' $filestampa la riga prima di una nuova riga vuota, uguale a tail -n 1 $file.


@don_crissti Ho bisogno di file che non hanno una riga vuota.
jcubic,

2
Posso chiederti il ​​motivo per cui devi trovare quei file? Immagino che abbia a che fare con il fatto che i file di testo in unix dovrebbero essere chiusi con una nuova riga (vi "quasi silenziosamente" aggiungerne uno quando si salva, per esempio), e diversi comandi (orientati al testo) ignoreranno il ultima riga se non termina con una nuova riga (wc, iirc .... ma ce ne sono altri). E questo può aiutare
Olivier Dulac,

awk 'END{print}' $file : questo ignora totalmente il contenuto di $ file e dopo aver finito di analizzare tutti i file contenuti in "$ file" aggiunge una nuova riga. Poiché è l'unica cosa che stampa il comando awk, potrebbe essere sostituito con: printf '\n'(senza alcun mentino di $ file) e fare la stessa cosa. Penso che questo NON sia quello a cui stavi puntando (es .: stampare l'ultima riga del file?)
Olivier Dulac,

@don_crissti: se l'ultimo carattere di un file non è una nuova riga, quel file non è esattamente posix un file TEXT unix. vedi: unix.stackexchange.com/a/263919/27616 . nota che molti comandi di testo (wc, per esempio) semplicemente ignorano quell'ultima "riga" se non è terminata da una nuova riga
Olivier Dulac,

1
@OlivierDulac: gawk stampa ce così fa FreeBSD, ma non avevo notato che è documentato come dipendente dall'implementazione : gnu.org/software/gawk/manual/… . Quindi non accada, ma non sempre.
dave_thompson_085,

Risposte:


14

Per chiarire, il carattere LF (aka \no newline) è il delimitatore di linea , non è il separatore di linea. Una linea non è finita a meno che non sia terminata da un carattere di nuova riga. Un file che contiene solo a\nbnon è un file di testo valido perché contiene caratteri dopo l'ultima riga. Lo stesso per un file che contiene solo a. Un file che contiene a\ncontiene una riga non vuota.

Quindi un file che termina con almeno una riga vuota termina con due caratteri di nuova riga o contiene un singolo carattere di nuova riga.

Se:

 tail -c 2 file | od -An -vtc

Emette \no \n \n, quindi il file contiene almeno una riga vuota finale. Se non genera nulla, allora si tratta di un file vuoto, se viene generato, <anything-but-\0> \ntermina in una riga non vuota. Nient'altro, non è un file di testo.

Ora, per usarlo per trovare i file che finiscono in una riga vuota, OK è efficace (specialmente per file di grandi dimensioni) in quanto legge solo gli ultimi due byte dei file, ma prima l'output non è facilmente analizzabile a livello di codice soprattutto considerando che è non coerente da un'implementazione odalla successiva e avremmo bisogno di eseguirne una taile una odper file.

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

(per trovare i file che terminano in una riga vuota) eseguiremo il minor numero possibile di comandi ma significherebbe leggere l'intero contenuto di tutti i file.

Idealmente, avresti bisogno di una shell in grado di leggere la fine di un file da sola.

Con zsh:

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

un modo per utilizzare il metodo di questa risposta per sapere se un certo file (s) sono file di testo: are_textfiles () { nontext=0; rem="return 0 if all args are files with terminating newline, or n [=number of non-textfiles]" ; for f in "$@" ; do [ -f "$f" ] && { tail -c 1 "$f" | od -An -vtc | grep "\\n" ;} >/dev/null 2>&1 || ((nontext++)) ; done ; return $nontext ; }. Utilizzare come:if ( are_textfiles this that otherthing ) ; then echo all are text files ; else echo "are_textfiles returned : $?" ; fi
Olivier Dulac il

6

Con gnu sede una shell come zsh(o bashcon shopt -s globstar):

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

questo controlla se l'ultima riga di ogni file non è vuota, in tal caso stampa il nome file.
Se vuoi il contrario (stampa i nomi dei file se l'ultima riga è vuota) sostituiscili /./con/^$/


1
Mai visto -sin azione prima. Grazie GNU!
Glenn Jackman,

Nota: l'opzione F esiste dalla versione sed 4.2.2 (22 dicembre 2012)
Isaac,

3

Un file di testo correttamente terminato con un'ultima riga vuota termina in due \n.

Quindi, ci aspettiamo che tail -c2debba essere uguale a $'\n\n'.

Purtroppo le espansioni di comando rimuovono le nuove righe finali. Avremo bisogno di un po 'di modifiche.

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"

Potremmo anche espanderci un po 'per verificare quali file non hanno una nuova riga finale:

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

Si noti che la nuova riga potrebbe essere cambiata in qualcosa come $'\r\nse necessario.
In tal caso, passare anche tail -c2a tail -c4.


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

1
questo non funziona con file vuoti ma posso conviverci.
jcubic,

Potrebbero esserci altri errori perché il confronto delle stringhe non sembra funzionare come mi aspettavo. Ho aggiunto un controllo per i file vuoti.
Oskar Skog,

Ah, ignora i caratteri di nuova riga.
Oskar Skog,

Si consideri il più leggibile cat $file 2>&1 /dev/null, o se questo è Bash-only, cat $file &> /dev/null.
gatto,

1
Inoltre, considera di citare $fileovunque sia usato - e per favore, usa $(commands ...)invece di `backticks`...
cat
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.