Làm thế nào để tìm tập tin không có dòng trống ở cuối?


9

Tôi có các tệp trong thư mục con của thư mục hiện tại có thể có hoặc không có dòng mới ở cuối; Làm cách nào tôi có thể tìm thấy các tệp không có dòng mới ở cuối?

Tôi đã thử điều này:

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

nhưng nó không hoạt động. awk 'END{print}' $filein dòng trước một dòng mới trống, giống như tail -n 1 $file.


@don_crissti Tôi cần các tệp không có dòng trống.
jcubic

2
Tôi có thể hỏi lý do bạn cần tìm những tập tin đó không? Tôi đoán nó có liên quan đến thực tế là các tệp Văn bản trong unix được cho là bị chấm dứt bằng một dòng mới (vi sẽ "gần như âm thầm" thêm một khi bạn lưu, chẳng hạn) và một số lệnh (hướng văn bản) sẽ bỏ qua dòng cuối cùng nếu nó không bị chấm dứt bởi một dòng mới (wc, iirc .... nhưng có những dòng khác). Và điều này có thể giúp
Olivier Dulac

awk 'END{print}' $file : điều này bỏ qua hoàn toàn nội dung của tệp $ và sau khi hoàn thành phân tích cú pháp tất cả các tệp có trong "tệp $", nó sẽ thêm một dòng mới. Vì nó là thứ duy nhất mà lệnh awk in ra, nó có thể được thay thế bằng: printf '\n'(hoàn toàn không có tập tin mentino nào của tập tin $) và làm điều tương tự. Tôi nghĩ rằng đây KHÔNG phải là những gì bạn đã nhắm đến (ví dụ: in dòng cuối cùng của tệp?)
Olivier Dulac

@don_crissti: nếu ký tự cuối cùng của tệp không phải là dòng mới, thì tệp đó không hoàn toàn là một tệp văn bản unix. xem: unix.stackexchange.com/a/263919/27616 . lưu ý rằng nhiều lệnh văn bản (ví dụ: wc) chỉ cần bỏ qua "dòng" cuối cùng nếu nó không bị chấm dứt bởi một dòng mới
Olivier Dulac

1
@OlivierDulac: gawk in cvà FreeBSD cũng vậy, nhưng tôi không nhận thấy nó được ghi nhận là phụ thuộc vào việc triển khai: gnu.org/software/gawk/manual/ . Vì vậy, nó không xảy ra nhưng không phải lúc nào.
dave_thndry_085

Câu trả lời:


14

Để làm rõ, ký tự LF (còn gọi là \ndòng mới) là dấu phân cách dòng , nó không phải là dấu phân cách dòng. Một dòng chưa kết thúc trừ khi nó bị chấm dứt bởi một ký tự dòng mới. Một tệp chỉ chứa a\nbkhông phải là tệp văn bản hợp lệ vì nó chứa các ký tự sau dòng cuối cùng. Tương tự cho một tập tin chỉ chứa a. Một tệp có a\nchứa một dòng không trống.

Vì vậy, một tệp kết thúc với ít nhất một dòng trống kết thúc bằng hai ký tự dòng mới hoặc chứa một ký tự dòng mới.

Nếu:

 tail -c 2 file | od -An -vtc

Đầu ra \nhoặc \n \n, sau đó tệp chứa ít nhất một dòng trống. Nếu nó xuất ra không có gì, thì đó là một tệp trống, nếu nó xuất ra <anything-but-\0> \n, thì nó kết thúc trong một dòng không trống. Bất cứ điều gì khác, nó không phải là một tập tin văn bản.

Bây giờ, để sử dụng điều đó để tìm các tệp kết thúc trong một dòng trống, OK đó là hiệu quả (đặc biệt đối với các tệp lớn) ở chỗ nó chỉ đọc hai byte cuối cùng của tệp, nhưng trước tiên, đầu ra không dễ dàng phân tích được bằng lập trình, đặc biệt là xem xét rằng nó không nhất quán từ một triển khai odtiếp theo và chúng tôi cần chạy một tailvà một odcho mỗi tệp.

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

(để tìm các tệp kết thúc trong một dòng trống) sẽ chạy càng ít lệnh càng tốt nhưng có nghĩa là đọc toàn bộ nội dung của tất cả các tệp.

Lý tưởng nhất là bạn cần một trình bao có thể tự đọc phần cuối của tệp.

Với zsh:

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

một cách để sử dụng phương thức của câu trả lời này để biết nếu một số tệp là tệp văn bản : 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 ; }. Sử dụng như:if ( are_textfiles this that otherthing ) ; then echo all are text files ; else echo "are_textfiles returned : $?" ; fi
Olivier Dulac

6

Với gnu sedvà một vỏ như zsh(hoặc bashvới shopt -s globstar):

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

cái này kiểm tra xem dòng cuối cùng của mỗi tệp không trống, nếu vậy nó sẽ in tên tệp.
Nếu bạn muốn ngược lại (in tên tệp nếu dòng cuối cùng trống) chỉ cần thay thế /./bằng/^$/


1
Chưa từng thấy -strong hành động trước đây. Cảm ơn GNU!
glenn jackman

Lưu ý: Tùy chọn F tồn tại từ phiên bản sed 4.2.2 (ngày 22 tháng 12 năm 2012)
Isaac

3

Một tệp văn bản kết thúc chính xác với một dòng cuối cùng trống kết thúc bằng hai \n.

Sau đó, chúng tôi hy vọng rằng tail -c2phải bằng $'\n\n'.

Đáng buồn mở rộng lệnh loại bỏ dòng mới. Chúng tôi sẽ cần một chút tinh chỉnh.

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"

Chúng tôi thậm chí có thể mở rộng một chút để kiểm tra xem tập tin nào không có dòng mới:

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

Lưu ý rằng dòng mới có thể được thay đổi thành một cái gì đó như $'\r\nnếu cần.
Trong trường hợp đó, cũng thay đổi tail -c2thành 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
Điều này không làm việc với các tập tin trống nhưng tôi có thể sống với điều đó.
jcubic

Có thể có thêm một số lỗi vì so sánh chuỗi dường như không hoạt động theo cách tôi mong đợi. Tôi đã thêm một kiểm tra cho các tập tin trống.
Oskar Skog

Ah, nó bỏ qua các ký tự dòng mới.
Oskar Skog

Hãy xem xét càng dễ đọc hơn cat $file 2>&1 /dev/null, hoặc nếu đây chỉ là Bash , cat $file &> /dev/null.
con mèo

1
Ngoài ra, hãy xem xét trích dẫn $fileở mọi nơi nó được sử dụng - và xin vui lòng, sử dụng $(commands ...)thay vì `backticks`...
con mèo
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.