Làm cách nào để tìm các tệp có 100% ký tự NUL trong nội dung của chúng?


15

Lệnh dòng lệnh Linux có thể xác định các tệp như vậy là gì?

AFAIK findlệnh (hoặc grep) chỉ có thể khớp với một chuỗi cụ thể bên trong tệp văn bản. Nhưng tôi muốn khớp toàn bộ nội dung, tức là tôi muốn xem tệp nào khớp với biểu thức chính quy \0+, bỏ qua (các) ký tự kết thúc dòng . Có lẽ find . cat | grepthành ngữ có thể hoạt động, nhưng tôi không biết làm thế nào để grep bỏ qua các dòng (và coi tệp là nhị phân).

Bối cảnh: Cứ sau vài ngày, khi máy tính xách tay của tôi đóng băng, phân vùng btrfs của tôi lại mất thông tin: các tệp được mở để ghi sẽ thay thế nội dung của chúng bằng số không (kích thước của tệp vẫn còn nguyên vẹn ít nhiều). Tôi sử dụng đồng bộ hóa và tôi không muốn các tệp giả mạo này lan truyền: Tôi cần một cách để xác định chúng để tôi có thể lấy chúng từ bản sao lưu.


bạn có nghĩa là các tập tin có số không trong đó?
Rahul Patil

1
Tôi nghĩ đó là về các ký tự NULL chứ không phải là số 0.
gertvdijk

9
Hãy lùi lại một bước ở đây. Cứ vài ngày, khi laptop của bạn đóng băng? Tại sao chúng ta không cố gắng khắc phục điều đó , vấn đề thực sự ở đây?
D_Bye

2
@D_Bye đó là ý tưởng tốt, nhưng cho đến nay nó đã không đi quá xa: [ unix.stackexchange.com/questions/57894/ Kẻ
Adam Ryczkowski

1
bạn đã xem xét -vtùy chọn grep: lọc tất cả các tệp có bất kỳ byte 1 đến 255.
ctrl-alt-delor

Câu trả lời:


10

Bạn có thể grepcho ␀ ký tự bằng chế độ regl Perl:

$ echo -ne "\0\0" > nul.bin
$ echo -ne "\0x\0" > non-nul.bin
$ grep -P "[^\0]" *.bin
Binary file non-nul.bin matches

Vì vậy, bạn có thể sử dụng này:

for path in *.foo
do
    grep -P "[^\0]" "$path" || echo "$path"
done

Tôi nhận được kết quả bất ngờ, sử dụng GNU grep 2.5.4. Bất kể tôi sử dụng --binary-files=texthay --binary-files=binary, nó đều cho truekết quả cho tất cả các giá trị dữ liệu không trống, ví dụ: "\0\0", "\0x\0", "abcd"... Các mã chính xác tôi sử dụng là: for typ in binary text ;do for dat in '\0\0' '\0x\0' 'abcd' '' ;do printf "$dat" >f; grep --binary-files=$typ -P '[^\0]' f >/dev/null && echo true || echo false; done; done
Peter.O

1
Bây giờ tôi đã cố gắng hơn nữa GNU grep) 2.10. Phiên bản mới hơn này cho kết quả như mong đợi ... vì vậy, +1 muộn màng
Peter.O

Thất bại trên một tập tin được tạo ra với printf '\0\n\0\0\n\n' > filehoặc printf '\n' > filecho vấn đề đó.
Stéphane Chazelas

1
@ StéphaneChazelas OP đã nói "bỏ qua (các) ký tự kết thúc dòng." Vì vậy, bất kỳ tệp nào chỉ bao gồm các ký tự \0\nký tự (thậm chí bằng 0) sẽ không khớp.
l0b0

5

Tôi đồng ý với những gì D_Bye nói về việc tìm ra gốc rễ của vấn đề.

Dù sao để kiểm tra xem một tập tin chỉ chứa \0và / hoặc \nbạn có thể sử dụng tr:

<file tr -d '\0\n' | wc -c

Trả về 0 cho null / newline và các tập tin trống.


2
tr -d '\0\n'giải quyết vấn đề về dòng mới, sau đó chỉ để lại vấn đề (?) của các tệp trống được liệt kê trong đầu ra ... Nó xử lý từng byte của mỗi tệp mặc dù (có thể hoặc không phải là vấn đề) +1
Peter.O

@ Peter.O: Tôi đã bỏ lỡ yêu cầu mới, cảm ơn bạn. Giải pháp này không được tối ưu hóa cho lắm và nếu chạy trên nhiều dữ liệu thì sẽ tốt hơn với giải pháp chuyển sang tìm các byte không phù hợp.
Thor

Nó hoạt động rất tốt. Tôi là trường hợp của tôi, tôi chỉ phải đảm bảo loại trừ các tệp có độ dài bằng không. Cảm ơn bạn.
Adam Ryczkowski

1
Tuy nhiên, điều này cũng sẽ tính các tệp có dòng mới là "trống".
Chris Xuống

@ChrisDown: Tôi đã làm cho văn bản trả lời rõ ràng như những gì nó làm. Không rõ OP muốn làm gì với các tệp chỉ có dòng mới.
Thor

4

Đây là một chương trình python nhỏ có thể làm điều đó:

import sys
def chunkCheck(fileObject, chunkSize=1024):
    while True:
        data = fileObject.read(chunkSize)
        if not data:
            return False
        if data.strip("\0"):
            return True
sys.exit(chunkCheck(open(sys.argv[1])))

Và trong hành động:

$ printf '\0\0\0' > file
$ ./onlynulls file && echo "Only nulls" || echo "Non-null characters"
Only nulls
$ printf a >> file
$ ./onlynulls file && echo "Only nulls" || echo "Non-null characters"
Non-null characters

Bạn có thể kiểm tra nhiều file bằng cách sử dụng tìm của -exec, xargs, GNU parallel, và các chương trình tương tự. Ngoài ra, điều này sẽ in tên tệp cần được xử lý:

files=( file1 file2 )
for file in "${files[@]}"; do
    ./onlynulls "$file" || printf '%s\n' "$file"
done

Hãy nhớ rằng nếu bạn sẽ chuyển đầu ra của chương trình này sang một chương trình khác, tên tệp có thể chứa các dòng mới, vì vậy bạn nên phân định nó theo cách khác (phù hợp, với \0).

Nếu bạn có nhiều tệp, sẽ tốt hơn nếu sử dụng tùy chọn để xử lý song song, vì điều này chỉ đọc một tệp tại một thời điểm.


4

Tôi nghi ngờ các tệp đó là thưa thớt, đó là chúng không có bất kỳ dung lượng đĩa nào được phân bổ cho chúng, chúng chỉ xác định kích thước tệp ( dusẽ báo cáo 0 cho chúng).

Trong trường hợp đó, với GNU find, bạn có thể làm (giả sử không có đường dẫn tệp nào chứa ký tự dòng mới):

find . -type f -size +0 -printf '%b:%p\n' | grep '^0:' | cut -d: -f2-

Điểm tốt. Tôi chưa bao giờ nghĩ về nó. Tôi sẽ thử. Việc sử dụng dusẽ ngăn không làm trầy xước nội dung của từng tệp trong hệ thống tệp, do đó toàn bộ quy trình sẽ không mất hơn 30 phút để hoàn thành.
Adam Ryczkowski

(và printf %bở trên báo cáo những gì dusẽ báo cáo)
Stéphane Chazelas

3

Tìm các tệp chỉ chứa ký tự null '\ 0' và ký tự dòng mới '\ n'.
Việc qin sed khiến mỗi tìm kiếm tệp thoát ngay lập tức khi tìm thấy bất kỳ ký tự không null nào trong một dòng.

find -type f -name 'file-*' |
  while IFS= read -r file ;do 
      out=$(sed -n '1=; /^\x00\+$/d; i non-null
                      ; q' "$file")
      [[ $out == "1" ]] &&  echo "$file"
  done

Tạo tập tin kiểm tra

> file-empty
printf '%s\n' 'line1' 'line2' 'line3'      > file-with-text           
printf '%4s\n' '' '' xx | sed 's/ /\x00/g' > file-with-text-and-nulls
printf '%4s\n' '' '' '' | sed 's/ /\x00/g' > file-with-nulls-and-newlines
printf '%4s'   '' '' '' | sed 's/ /\x00/g' > file-with-nulls-only

đầu ra

./file-with-nulls-and-newlines
./file-with-nulls-only

1

Lớp lót này là cách hiệu quả nhất để tìm các tệp 100% bằng cách sử dụng GNU find, xargs và GNU grep:

find . -type f -size +1 -readable -print0 | xargs -0 grep -LP "[^\x00]"

Ưu điểm của phương pháp này so với các câu trả lời được cung cấp khác là:

  • tập tin không thưa thớt được bao gồm trong tìm kiếm.
  • các tệp không thể đọc được không được chuyển đến grep, tránh Permission deniedcác cảnh báo.
  • grep sẽ dừng đọc dữ liệu từ các tệp sau khi tìm thấy bất kỳ byte nào.
  • các tệp trống (byte không) không được bao gồm trong kết quả.
  • Ít grepquá trình kiểm tra hiệu quả nhiều tập tin.
  • kết quả có thể được chuyển qua để xargsxử lý tiếp.
  • hoạt động trên hầu hết các hệ thống nhúng thiếu Python / Perl.

Việc chuyển -Ztùy chọn sang grepvà sử dụng xargs -0 ...cho phép các hành động tiếp theo được thực hiện trên các tệp 100% (ví dụ: dọn dẹp):

find . -type f -size +1 -readable -print0 | xargs -0 grep -ZLP "[^\x00]" | xargs -0 rm

Tôi cũng khuyên bạn nên sử dụng các findtùy chọn -Pđể tránh theo các liên kết tượng trưng và -xdevđể tránh truyền qua các hệ thống tệp (ví dụ: gắn kết từ xa, cây thiết bị, gắn kết liên kết, v.v.).

Để bỏ qua (các) ký tự kết thúc dòng , biến thể sau sẽ hoạt động (mặc dù tôi không nghĩ đây là một ý tưởng hay):

find . -type f -size +1 -readable -print0 | xargs -0 grep -LP "[^\x00\r\n]"

Kết hợp tất cả lại với nhau, bao gồm loại bỏ các tệp không mong muốn (100% nul / ký tự dòng mới) để ngăn không cho chúng được sao lưu:

find -P . -xdev -type f -size +1 -readable -print0 | xargs -0 grep -ZLP "[^\x00\r\n]" | xargs -0 rm

Tôi không khuyên bạn nên bao gồm các tệp trống (byte không), chúng thường tồn tại cho các mục đích rất cụ thể .


Trở thành người nhanh nhất trong số rất nhiều lựa chọn thay thế là một tuyên bố táo bạo. Tôi sẽ đánh dấu câu trả lời của bạn là được chấp nhận nếu bạn thêm điểm chuẩn :-)
Adam Ryczkowski

0

Con trăn

Tập tin duy nhất

Xác định bí danh:

alias is_binary="python -c 'import sys; sys.exit(not b\"\x00\" in open(sys.argv[1], \"rb\").read())'"

Kiểm tra nó:

$ is_binary /etc/hosts; echo $?
1
$ is_binary `which which`; echo $?
0

Nhiều tập tin

Tìm tất cả các tệp nhị phân đệ quy:

IS_BINARY='import sys; sys.exit(not b"\x00" in open(sys.argv[1], "rb").read())'
find . -type f -exec bash -c "python -c '$IS_BINARY' {} && echo {}" \;

Để tìm tất cả các tệp không nhị phân, thay đổi &&với ||.


0

Để sử dụng GNU sed, bạn có thể sử dụng -ztùy chọn, định nghĩa một dòng là các chuỗi kết thúc bằng 0 và khớp và xóa các dòng trống như vậy:

if [ "$( sed -z '/^$/d' "$file" | head -c 1 | wc -c )" -eq 0 ]; then
    echo "$file contains only NULL!"
fi

Lệnh head inb between chỉ là một tối ưu hóa.

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.