Bagaimana menemukan file yang tidak memiliki baris kosong di akhir?

9

Saya memiliki file di subdirektori dari direktori saat ini yang mungkin atau mungkin tidak memiliki baris baru di akhir; bagaimana saya bisa menemukan file yang tidak memiliki baris baru di akhir?

Saya sudah mencoba ini:

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

tetapi tidak berhasil. awk 'END{print}' $filemencetak baris sebelum baris baru yang kosong, sama dengan tail -n 1 $file.

jcubic
sumber
@don_crissti Saya perlu file yang tidak memiliki jalur kosong.
jcubic
2
Bolehkah saya bertanya alasan Anda perlu menemukan file-file itu? Saya kira itu ada hubungannya dengan fakta bahwa file teks di unix seharusnya diakhiri dengan baris baru (vi akan "hampir diam-diam" menambahkan satu ketika Anda menyimpan, misalnya), dan beberapa perintah (berorientasi teks) akan mengabaikan baris terakhir jika tidak diakhiri oleh baris baru (wc, iirc .... tetapi ada yang lain). Dan ini mungkin membantu
Olivier Dulac
awk 'END{print}' $file : ini benar-benar mengabaikan isi $ file, dan setelah menyelesaikan parsing semua file yang terkandung dalam "$ file" itu menambahkan baris baru. Karena ini adalah satu-satunya yang dicetak perintah awk, maka dapat diganti dengan: printf '\n'(tanpa mentino $ file sama sekali) dan melakukan hal yang sama. Saya pikir ini BUKAN apa yang Anda tuju (yaitu: cetak baris terakhir file?)
Olivier Dulac
@don_crissti: jika karakter terakhir dari sebuah file bukan baris baru, maka file tersebut tidak secara langsung menjadi file TEXT unix. lihat: unix.stackexchange.com/a/263919/27616 . perhatikan bahwa banyak perintah teks (wc, misalnya) abaikan saja "baris" terakhir itu jika tidak diakhiri oleh baris baru
Olivier Dulac
1
@OlivierDulac: cetakan gawk cdan begitu juga FreeBSD, tapi saya tidak melihat itu didokumentasikan sebagai tergantung implementasi: gnu.org/software/gawk/manual/… . Jadi itu memang terjadi tetapi tidak selalu.
dave_thompson_085

Jawaban:

14

Untuk memperjelas, karakter LF (alias \natau baris baru) adalah pembatas garis , itu bukan pemisah garis. Garis tidak selesai kecuali diakhiri oleh karakter baris baru. File yang hanya berisi a\nbbukan file teks yang valid karena berisi karakter setelah baris terakhir. Sama untuk file yang hanya berisi a. File yang berisi a\nberisi satu baris tidak kosong.

Jadi file yang diakhiri dengan setidaknya satu baris kosong berakhir dengan dua karakter baris baru atau berisi satu karakter baris baru.

Jika:

 tail -c 2 file | od -An -vtc

Keluaran \natau \n \n, maka file tersebut berisi setidaknya satu baris kosong yang tertinggal. Jika tidak menghasilkan apa-apa, maka itu file kosong, jika itu output <anything-but-\0> \n, maka itu berakhir pada baris yang tidak kosong. Yang lainnya, ini bukan file teks.

Sekarang, untuk menggunakannya untuk menemukan file yang berakhir pada baris kosong, OK itu efisien (terutama untuk file besar) karena hanya membaca dua byte terakhir dari file, tetapi pertama-tama output tidak mudah diurai secara terprogram terutama mengingat bahwa itu adalah tidak konsisten dari satu implementasi odke yang berikutnya, dan kita perlu menjalankan satu taildan satu odper file.

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

(untuk menemukan file yang berakhir pada baris kosong) akan menjalankan perintah sesedikit mungkin tetapi berarti membaca konten lengkap dari semua file.

Idealnya, Anda membutuhkan sebuah shell yang dapat membaca akhir file dengan sendirinya.

Dengan zsh:

zmodload zsh/system
for f (**/*(D.L+0)) {
  {
    sysseek -w end -2
    sysread
    [[ $REPLY = $'\n' || $REPLY = $'\n\n' ]] && print -r -- $f
  } < $f
}
Stéphane Chazelas
sumber
cara untuk menggunakan metode ini jawaban untuk mengetahui apakah beberapa berkas (s) adalah file teks: 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 ; }. Gunakan sebagai:if ( are_textfiles this that otherthing ) ; then echo all are text files ; else echo "are_textfiles returned : $?" ; fi
Olivier Dulac
6

Dengan gnu seddan shell seperti zsh(atau bashdengan shopt -s globstar):

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

ini memeriksa apakah baris terakhir dari setiap file tidak kosong, jika demikian ia mencetak nama file.
Jika Anda menginginkan yang sebaliknya (cetak nama file jika baris terakhir kosong) ganti saja /./dengan/^$/

don_crissti
sumber
1
Belum pernah terlihat -sberaksi sebelumnya. GNU terima kasih!
glenn jackman
Catatan: Opsi F ada dari versi sed 4.2.2 (22 Desember 2012)
Isaac
3

File teks yang dihentikan dengan benar dengan baris terakhir yang kosong berakhir menjadi dua \n.

Kemudian, kami berharap itu tail -c2harus sama dengan $'\n\n'.

Sayangnya ekspansi perintah menghapus garis baru. Kami perlu sedikit penyesuaian.

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"

Kami bahkan dapat memperluas sedikit untuk memeriksa file mana yang gagal memiliki baris baru yang tertinggal:

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

Perhatikan bahwa baris baru dapat diubah menjadi sesuatu seperti $'\r\njika diperlukan.
Dalam hal itu, ubah juga tail -c2ke tail -c4.

Ishak
sumber
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
Oskar Skog
sumber
1
ini tidak berfungsi dengan file kosong tapi saya bisa hidup dengan itu.
jcubic
Mungkin ada beberapa kesalahan karena perbandingan string tampaknya tidak berfungsi seperti yang saya harapkan. Saya menambahkan cek untuk file kosong.
Oskar Skog
Ah, itu mengabaikan karakter baris baru.
Oskar Skog
Pertimbangkan yang lebih mudah dibaca cat $file 2>&1 /dev/null, atau jika ini hanya Bash cat $file &> /dev/null,.
kucing
1
Juga, pertimbangkan untuk mengutip di $filemana saja yang digunakan - dan tolong, gunakan $(commands ...)sebagai ganti `backticks`...
cat