xác định các tệp có ký tự không phải ASCII hoặc không in được trong tên tệp


24

Trong một thư mục có kích thước 80GB với khoảng 700.000 tệp, có một số tên tệp có các ký tự không phải tiếng Anh trong tên tệp. Khác với việc truy tìm danh sách tập tin một cách tốn công:

  • Một cách dễ dàng để liệt kê hoặc xác định các tên tập tin này?
  • Một cách để tạo các ký tự không phải tiếng Anh có thể in được - những ký tự không được liệt kê trong phạm vi có thể in được man ascii(vì vậy tôi có thể kiểm tra xem các tệp này có được xác định không)?

Câu trả lời:


32

Giả sử rằng "nước ngoài" có nghĩa là "không phải là ký tự ASCII", thì bạn có thể sử dụng findvới một mẫu để tìm tất cả các tệp không có ký tự ASCII có thể in trong tên của chúng:

LC_ALL=C find . -name '*[! -~]*'

(Không gian là ký tự có thể in đầu tiên được liệt kê trên http://www.asciitable.com/ , ~là ký tự cuối cùng.)

Gợi ý cho LC_ALL=Clà bắt buộc (thực tế LC_CTYPE=CLC_COLLATE=C), nếu không, phạm vi ký tự được giải thích không chính xác. Xem thêm trang hướng dẫn glob(7). Vì các LC_ALL=Cnguyên nhân findđể giải thích các chuỗi là ASCII, nó sẽ in các ký tự nhiều byte (chẳng hạn như π) dưới dạng các dấu hỏi. Để khắc phục điều này, chuyển sang một số chương trình (ví dụ cat) hoặc chuyển hướng đến tệp.

Thay vì chỉ định phạm vi ký tự, [:print:]cũng có thể được sử dụng để chọn "ký tự có thể in". Hãy chắc chắn đặt ngôn ngữ C hoặc bạn có hành vi khá (dường như) tùy ý.

Thí dụ:

$ touch $(printf '\u03c0') "$(printf 'x\ty')"
$ ls -F
dir/  foo  foo.c  xrestop-0.4/  xrestop-0.4.tar.gz  π
$ find -name '*[! -~]*'       # this is broken (LC_COLLATE=en_US.UTF-8)
./x?y
./dir
./π
... (a lot more)
./foo.c
$ LC_ALL=C find . -name '*[! -~]*'
./x?y
./??
$ LC_ALL=C find . -name '*[! -~]*' | cat
./x y
./π
$ LC_ALL=C find . -name '*[![:print:]]*' | cat
./x y
./π

1
Xin lưu ý rằng bạn có tên tệp đang sử dụng các bộ ký tự nước ngoài không tương thích với UTF-8 hoặc ASCII. Trong những trường hợp đó, bạn có thể thấy dấu chấm hỏi thay vì ký tự.
Lekensteyn

1
+1, nhưng tôi sẽ sử dụng LC_ALL=Cthay vì LC_COLLATE=Ckhông có ý nghĩa gì khi đặt LC_COLLATE thành C mà không cài đặt LC_CTYPEvà để đảm bảo nó vẫn hoạt động ngay cả khi biến LC_ALL ở trong môi trường.
Stéphane Chazelas

Nếu SPCcó thể in được , sau đó những gì về TABLFđó cũng thường được tìm thấy trong các tập tin văn bản?
Stéphane Chazelas

1
Cảm ơn - điều này đã tìm thấy sáu tệp, có dấu gạch nối dài, dấu gạch nối ngắn và một biến thể của trích dẫn đơn. Những thứ này đều có nguồn gốc từ MS Word. Không có sự khác biệt trong các tệp được liệt kê giữa LC_ALL và LC_COLLATE. LC_COLLATE hiển thị chính xác các ký tự không phải ASCII trong khi LC_ALL hiển thị ??? thay thế. Câu trả lời tuyệt vời!
suspectus

1
@suspectus Tôi cập nhật bằng câu trả lời dựa trên những gợi ý từ Stephane. Đối với LC_COLLATELC_CTYPE, xem thêm các find(1)trang.
Lekensteyn

6

Nếu bạn dịch từng tên tệp bằng cách sử dụng tr -d '[\200-\377]'và so sánh nó với tên gốc, thì bất kỳ tên tệp nào có ký tự đặc biệt sẽ không giống nhau.

(Trên đây giả sử rằng bạn có nghĩa là không phải ASCII với nước ngoài)


2
Điều đó cũng loại bỏ []trong hầu hết các trthực hiện.
Stéphane Chazelas

Có - nó đã loại bỏ []trên hệ thống của tôi.
suspectus

+1 - giải pháp đã tìm thấy tất cả (sáu) tên tệp có ký hiệu không phải ASCII (ngoài []). cảm ơn.
suspectus

3

Bạn có thể sử dụng trđể xóa bất kỳ ký tự nước ngoài nào khỏi tên tệp và so sánh kết quả với tên tệp gốc để xem nó có chứa các ký tự nước ngoài hay không.

find . -type f > filenames
while read filename; do
      stripped="$(printf '%s\n' "$filename" | tr -d -C '[[:alnum:]][[:space:]][[:punct:]]')"
      test "$filename" = "$stripped" || printf '%s\n' "$filename"; 
done < filenames

4
đó là một phần mở rộng tuyệt vời cho câu trả lời của tôi, nhưng nó quá đơn giản, tên tệp có thể có dòng mới trong đó và sau đó tập lệnh của bạn sẽ không hoạt động
Timo

1
Nếu bạn muốn findđầu ra sau quá trình , hãy sử dụng đầu ra / đầu vào kết thúc NUL như trong câu trả lời này .
Lekensteyn

0

Câu trả lời được chấp nhận là hữu ích, nhưng nếu tên tệp của bạn đã có trong mã hóa được chỉ định trong LANG/ LC_CTYPE, tốt hơn là chỉ nên làm:

LC_COLLATE=C find . -name '*[! -~]*'

Các lớp ký tự bị ảnh hưởng bởi LC_CTYPE, nhưng lệnh trên không sử dụng các lớp ký tự, chỉ phạm vi, vì vậy LC_CTYPEchỉ ngăn các ký tự bất thường bị thay thế bằng dấu chấm hỏi.

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.