Làm cách nào để grep cho tất cả các ký tự không phải ASCII?


359

Tôi có một số tệp XML rất lớn và tôi đang cố gắng tìm các dòng có chứa các ký tự không phải ASCII. Tôi đã thử như sau:

grep -e "[\x{00FF}-\x{FFFF}]" file.xml

Nhưng điều này trả về mọi dòng trong tệp, bất kể dòng đó có chứa một ký tự trong phạm vi được chỉ định hay không.

Tôi có sai cú pháp hay tôi đang làm gì khác sai? Tôi cũng đã thử:

egrep "[\x{00FF}-\x{FFFF}]" file.xml 

(với cả dấu ngoặc đơn và dấu ngoặc kép bao quanh mẫu).


Các ký tự ASCII chỉ dài một byte, vì vậy trừ khi tệp là unicode, không nên có các ký tự trên 0xFF.
zdav

Làm thế nào để chúng tôi đi trên \ xFF? Grep đưa ra lỗi "grep: phạm vi không theo thứ tự trong lớp ký tự".
Mudit Jain

Câu trả lời:


494

Bạn có thể sử dụng lệnh:

grep --color='auto' -P -n "[\x80-\xFF]" file.xml

Điều này sẽ cung cấp cho bạn số dòng, và sẽ làm nổi bật các ký tự không phải là ascii màu đỏ.

Trong một số hệ thống, tùy thuộc vào cài đặt của bạn, các cách trên sẽ không hoạt động, do đó bạn có thể grep bằng nghịch đảo

grep --color='auto' -P -n "[^\x00-\x7F]" file.xml

Cũng lưu ý rằng bit quan trọng là -Pcờ tương đương với --perl-regexp: vì vậy nó sẽ diễn giải mẫu của bạn dưới dạng biểu thức chính quy Perl. Nó cũng nói rằng

đây là thử nghiệm cao và grep -P có thể cảnh báo các tính năng chưa được thực hiện.


42
Điều này sẽ không hoạt động trong BSD grep(trên OS X 10.8 Mountain Lion), vì nó không hỗ trợ Ptùy chọn.
Bastiaan M. van de Weerd

20
Để cập nhật nhận xét cuối cùng của tôi, phiên bản GNU grepcó sẵn trong dupesthư viện của Homebrew (cho phép sử dụng brew tap homebrew/dupes):brew install grep
Bastiaan M. van de Weerd

48
@BastiaanVanDeWeerd là chính xác, grep trên OSX 10.8 không còn hỗ trợ PCRE ("biểu thức chính quy tương thích Perl") vì Darwin hiện sử dụng grep BSD thay vì GNU grep. Một cách khác để cài đặt dupesthư viện là cài đặt pcrethay thế: brew install pcre... như một phần của việc này, bạn sẽ nhận được pcregreptiện ích, bạn có thể sử dụng như sau:pcregrep --color='auto' -n "[\x80-\xFF]" file.xml
pvandenberk

15
Đối với brewngười dùng Mac , coreutils của GNU có thể được cài đặt brew install coreutils. Điều này sẽ cung cấp cho bạn rất nhiều công cụ GNU có tiền tố 'g' - trong trường hợp này sử dụng ggrep. Điều này sẽ tránh các vấn đề phát sinh từ việc thay thế một tiện ích hệ thống, vì các tập lệnh Mac dành riêng cho hệ thống hiện phụ thuộc vào grep BSD.
Joel Purra

22
Điều này hoạt động tốt trên máy mac ag "[\x80-\xFF]" filemà bạn chỉ cần cài đặtthe_silver_searcher
slf

123

Thay vì đưa ra các giả định về phạm vi byte của các ký tự không phải ASCII, như hầu hết các giải pháp trên đều làm, IMO tốt hơn một chút để rõ ràng về phạm vi byte thực tế của các ký tự ASCII.

Vì vậy, giải pháp đầu tiên chẳng hạn sẽ trở thành:

grep --color='auto' -P -n '[^\x00-\x7F]' file.xml

(về cơ bản là greps cho bất kỳ ký tự nào ngoài phạm vi ASCII thập lục phân: từ \ x00 đến \ x7F)

Trên Mountain Lion không hoạt động (do thiếu hỗ trợ PCRE trong BSD grep) , nhưng với pcrecài đặt qua Homebrew, những điều sau đây cũng sẽ hoạt động tốt:

pcregrep --color='auto' -n '[^\x00-\x7F]' file.xml

Bất kỳ ưu và nhược điểm mà bất cứ ai có thể nghĩ ra?


9
Điều này thực sự làm việc cho tôi khi các giải pháp trên thất bại. Tìm kiếm dấu nháy đơn M $ Word đã không dễ dàng hơn!
AlbertEngelB

2
Nếu bạn có trình bao tương thích bash nhưng không hoạt động với pcre-grep, hãy LC_COLLATE=C grep $'[^\1-\177]'hoạt động (đối với các tệp không có byte rỗng)
idupree

2
Giải pháp này dường như hoạt động ổn định hơn những giải pháp trên.
0xcaff

1
Tôi đã phải sử dụng điều này để lấy Kanji, Cyrillic và tiếng Trung Quốc truyền thống trong tệp UTF8 của mình, sử dụng "[\ x80- \ xFF]" đã bỏ lỡ tất cả những thứ này.
buckaroo1177125

1
Các pro là điều này làm việc xuất sắc trong khi các tùy chọn khác là tuyệt vời nhưng không tuyệt vời. Không có khuyết điểm được tìm thấy cho đến nay.
jwpfox

67

Các công việc sau đây cho tôi:

grep -P "[\x80-\xFF]" file.xml

Các ký tự không phải ASCII bắt đầu ở 0x80 và chuyển đến 0xFF khi nhìn vào byte. Grep (và gia đình) không thực hiện xử lý Unicode để hợp nhất các ký tự nhiều byte thành một thực thể duy nhất để khớp regex như bạn muốn. Các -Ptùy chọn trong grep của tôi cho phép sử dụng \xddthoát trong các lớp nhân vật để hoàn thành những gì bạn muốn.


1
Đối với chế độ xem có thể không biết ngay cách gọi này qua nhiều tệp, chỉ cần chạy: find. -name * .xml | xargs grep -P "[\ x80- \ xFF]"
David Mohundro

1
Điều này không trả về một trận đấu, nhưng không có dấu hiệu cho thấy nhân vật là gì và nó ở đâu. Làm thế nào để người ta thấy nhân vật là gì, và nó ở đâu?
Faheem Mitha

Thêm "-n" sẽ cho số dòng, các ký tự không nhìn thấy được sẽ hiển thị dưới dạng một khối tại thiết bị đầu cuối: grep -n -P "[\ x80- \ xFF]" file.xml
fooMonster

4
Tôi đang gặp vấn đề với Hangul Hàn Quốc: echo '소녀시대' | grep -P "[\x80-\xFF]"không trả lại gì cho tôi - có ai khác có thể xác nhận không? (GNU grep 2.21)
frabjous

@frabjous Tương tự ở đây, nhưng grepping các tác phẩm nghịch đảo : echo '소녀시대' | grep -P "[^\x00-\x7F]". Hoặc chỉ cần sử dụng the_silver_searchernhư ra nhọn bởi @slf:echo '소녀시대' | ag "[\x80-\xFF]"
psmith

55

Trong perl

perl -ane '{ if(m/[[:^ascii:]]/) { print  } }' fileName > newFile

1
Trên OSX10.11, tôi đã phải thử một số giải pháp grep + regex trước khi tìm thấy giải pháp này thực sự hoạt động
sg

Muốn chia sẻ giải pháp OSX đó @sg?!
địa lý

Kịch bản perl ở trên là giải pháp mà tôi đang nói đến
sg

5
perl -lne 'print if /[^[:ascii:]]/' file.xml
Naveed

43

Cách dễ dàng là xác định một ký tự không phải ASCII ... là một ký tự không phải là ký tự ASCII.

LC_ALL=C grep '[^ -~]' file.xml

Thêm một tab sau ^nếu cần thiết.

Đặt LC_COLLATE=Ctránh những bất ngờ khó chịu về ý nghĩa của phạm vi nhân vật ở nhiều địa phương. Việc cài đặt LC_CTYPE=Clà cần thiết để khớp các ký tự byte đơn - nếu không lệnh sẽ bỏ lỡ các chuỗi byte không hợp lệ trong mã hóa hiện tại. Cài đặt LC_ALL=Ctránh các hiệu ứng phụ thuộc cục bộ hoàn toàn.


Trên RedHat 6.4 với tcsh, tôi đã phải sử dụng <<< env LC_COLLATE = C grep -n '[^ - ~]' file.xml >>>. Tôi đã thêm -n để lấy số dòng.
ddevienne

Đối với tôi echo "A" | LC_COLLATE=C grep '[^ -~]'trả lại một trận đấu
frabjous

1
@frabjous Nếu bạn có LC_ALL=en_US.UTF-8, điều đó hơn hẳn các LC_COLLATEthiết lập. Bạn không nên có cái này trong môi trường của bạn! LC_ALLthường chỉ buộc một nhiệm vụ cụ thể sử dụng một ngôn ngữ cụ thể C. Để đặt ngôn ngữ mặc định cho tất cả các danh mục, hãy đặt LANG.
Gilles 'SO- ngừng trở nên xấu xa'

1
Lúc đầu, tôi không thêm LC_ALL=C, nó hoạt động khác trên Mac OS X và Ubuntu. Sau khi tôi thêm cài đặt này, họ cho kết quả tương tự.
Max Peng

1
Điều này hoạt động trên máy Mac, trong khi các giải pháp dựa trên grep khác thì không.
Matthias Fripp

26

Đây là một biến thể khác mà tôi thấy rằng tạo ra kết quả hoàn toàn khác với tìm kiếm grep [\x80-\xFF]trong câu trả lời được chấp nhận. Có lẽ nó sẽ hữu ích cho ai đó để tìm thêm các ký tự không phải ascii:

grep --color='auto' -P -n "[^[:ascii:]]" myfile.txt

Lưu ý: grep máy tính của tôi (máy Mac) không có -Ptùy chọn, vì vậy tôi đã thực hiện brew install grepvà bắt đầu cuộc gọi ở trên ggrepthay vì grep.


2
Đây là câu trả lời tốt nhất, vì nó hoạt động cho Mac cũng như Linux.
tommy.carstensen

Chỉ có một công việc cho tôi trên Linux.

9

Các mã sau hoạt động:

find /tmp | perl -ne 'print if /[^[:ascii:]]/'

Thay thế /tmpbằng tên của thư mục bạn muốn tìm kiếm thông qua.


2
Trên máy Mac, điều này hoạt động, trong khi hầu hết những người dựa trên grep thì không.
Matthias Fripp

9

Tìm kiếm các ký tự không in được. TLDR; Tóm tắt

  1. tìm kiếm ký tự điều khiển VÀ unicode mở rộng
  2. cài đặt ngôn ngữ, ví dụ LC_ALL=Ccần thiết để làm cho grep làm những gì bạn có thể mong đợi với unicode mở rộng

VÌ các công cụ tìm char không phải ascii ưa thích:

$ perl -ne 'print "$. $_" if m/[\x00-\x08\x0E-\x1F\x80-\xFF]/' notes_unicode_emoji_test

như trong câu trả lời hàng đầu, grep ngược:

$ grep --color='auto' -P -n "[^\x00-\x7F]" notes_unicode_emoji_test

như trong câu trả lời hàng đầu nhưng VỚI LC_ALL=C:

$ LC_ALL=C grep --color='auto' -P -n "[\x80-\xFF]" notes_unicode_emoji_test

. . hơn . . chi tiết thú vị về điều này :. . .

Tôi đồng ý với Harvey ở trên bị chôn vùi trong các bình luận, thường hữu ích hơn khi tìm kiếm các ký tự không in được HOẶC thật dễ dàng để nghĩ không phải ASCII khi bạn thực sự nên nghĩ rằng không thể in được. Harvey gợi ý "sử dụng cái này:" [^\n -~]". Thêm \ r cho các tệp văn bản DOS. Điều đó dịch thành" [^\x0A\x020-\x07E]"và thêm \ x0D cho CR"

Ngoài ra, việc thêm -c (hiển thị số mẫu được khớp) vào grep rất hữu ích khi tìm kiếm các ký tự không in được vì các chuỗi khớp có thể làm rối thiết bị đầu cuối.

Tôi thấy việc thêm phạm vi 0-8 và 0x0e-0x1f (vào phạm vi 0x80-0xff) là một mẫu hữu ích. Điều này không bao gồm TAB, CR và LF và một hoặc hai ký tự in không phổ biến khác. Vì vậy, IMHO một mẫu grep khá hữu ích (mặc dù thô) là mẫu NÀY:

grep -c -P -n "[\x00-\x08\x0E-\x1F\x80-\xFF]" *

HOẠT ĐỘNG, nói chung bạn sẽ cần phải làm điều này:

LC_ALL=C grep -c -P -n "[\x00-\x08\x0E-\x1F\x80-\xFF]" *

phá vỡ:

LC_ALL=C - set locale to C, otherwise many extended chars will not match (even though they look like they are encoded > 0x80)
\x00-\x08 - non-printable control chars 0 - 7 decimal
\x0E-\x1F - more non-printable control chars 14 - 31 decimal
\x80-1xFF - non-printable chars > 128 decimal
-c - print count of matching lines instead of lines
-P - perl style regexps

Instead of -c you may prefer to use -n (and optionally -b) or -l
-n, --line-number
-b, --byte-offset
-l, --files-with-matches

Ví dụ: ví dụ thực tế về việc sử dụng find để grep tất cả các tệp trong thư mục hiện tại:

LC_ALL=C find . -type f -exec grep -c -P -n "[\x00-\x08\x0E-\x1F\x80-\xFF]" {} + 

Bạn có thể muốn điều chỉnh grep nhiều lần. ví dụ: char (0x08 - backspace) char được sử dụng trong một số tệp có thể in hoặc để loại trừ VT (0x0B - tab dọc). Các ký tự BEL (0x07) và ESC (0x1B) cũng có thể được coi là có thể in được trong một số trường hợp.

Non-Printable ASCII Chars
** marks PRINTABLE but CONTROL chars that is useful to exclude sometimes
Dec   Hex Ctrl Char description           Dec Hex Ctrl Char description
0     00  ^@  NULL                        16  10  ^P  DATA LINK ESCAPE (DLE)
1     01  ^A  START OF HEADING (SOH)      17  11  ^Q  DEVICE CONTROL 1 (DC1)
2     02  ^B  START OF TEXT (STX)         18  12  ^R  DEVICE CONTROL 2 (DC2)
3     03  ^C  END OF TEXT (ETX)           19  13  ^S  DEVICE CONTROL 3 (DC3)
4     04  ^D  END OF TRANSMISSION (EOT)   20  14  ^T  DEVICE CONTROL 4 (DC4)
5     05  ^E  END OF QUERY (ENQ)          21  15  ^U  NEGATIVE ACKNOWLEDGEMENT (NAK)
6     06  ^F  ACKNOWLEDGE (ACK)           22  16  ^V  SYNCHRONIZE (SYN)
7     07  ^G  BEEP (BEL)                  23  17  ^W  END OF TRANSMISSION BLOCK (ETB)
8     08  ^H  BACKSPACE (BS)**            24  18  ^X  CANCEL (CAN)
9     09  ^I  HORIZONTAL TAB (HT)**       25  19  ^Y  END OF MEDIUM (EM)
10    0A  ^J  LINE FEED (LF)**            26  1A  ^Z  SUBSTITUTE (SUB)
11    0B  ^K  VERTICAL TAB (VT)**         27  1B  ^[  ESCAPE (ESC)
12    0C  ^L  FF (FORM FEED)**            28  1C  ^\  FILE SEPARATOR (FS) RIGHT ARROW
13    0D  ^M  CR (CARRIAGE RETURN)**      29  1D  ^]  GROUP SEPARATOR (GS) LEFT ARROW
14    0E  ^N  SO (SHIFT OUT)              30  1E  ^^  RECORD SEPARATOR (RS) UP ARROW
15    0F  ^O  SI (SHIFT IN)               31  1F  ^_  UNIT SEPARATOR (US) DOWN ARROW

CẬP NHẬT: Tôi đã phải xem lại điều này gần đây. Và, YYMV tùy thuộc vào cài đặt thiết bị đầu cuối / dự báo thời tiết mặt trời NHƯNG. . Tôi nhận thấy rằng grep không tìm thấy nhiều ký tự unicode hoặc mở rộng. Mặc dù theo trực giác, chúng phải khớp phạm vi 0x80 đến 0xff, các ký tự unicode 3 và 4 byte không khớp. ??? Bất cứ ai có thể giải thích điều này? ĐÚNG. @frabjous đã hỏi và @calandoa giải thích rằng LC_ALL=Cnên được sử dụng để đặt ngôn ngữ cho lệnh để tạo grep khớp.

ví dụ địa phương của tôi LC_ALL=trống

$ locale
LANG=en_IE.UTF-8
LC_CTYPE="en_IE.UTF-8"
.
.
LC_ALL=

grep với LC_ALL=các kết quả trống được mã hóa 2 byte nhưng không được mã hóa 3 và 4 byte:

$ grep -P -n "[\x00-\x08\x0E-\x1F\x80-\xFF]" notes_unicode_emoji_test
5 copyright c2a9
7:call  underscore c2a0
9:CTRL
31:5 © copyright
32:7 call  underscore

grep với LC_ALL=Cdường như khớp với tất cả các ký tự mở rộng mà bạn muốn:

$ LC_ALL=C grep --color='auto' -P -n "[\x80-\xFF]" notes_unicode_emoji_test  
1:���� unicode dashes e28090
3:��� Heart With Arrow Emoji - Emojipedia == UTF8? f09f9298
5:� copyright c2a9
7:call underscore c2a0
11:LIVE��E! ���������� ���� ���������� ���� �� �� ���� ����  YEOW, mix of japanese and chars from other e38182 e38184 . . e0a487
29:1 ���� unicode dashes
30:3 ��� Heart With Arrow Emoji - Emojipedia == UTF8 e28090
31:5  copyright
32:7 call underscore
33:11 LIVE��E! ���������� ���� ���������� ���� �� �� ���� ����  YEOW, mix of japanese and chars from other
34:52 LIVE��E! ���������� ���� ���������� ���� �� �� ���� ����  YEOW, mix of japanese and chars from other
81:LIVE��E! ���������� ���� ���������� ���� �� �� ���� ����  YEOW, mix of japanese and chars from other

Kết hợp perl NÀY (được tìm thấy một phần ở nơi khác trên stackoverflow) HOẶC grep ngược trên câu trả lời hàng đầu DO dường như tìm thấy TẤT CẢ các ký tự ~ lạ ~ và ~ tuyệt vời ~ "không phải ascii" mà không đặt ngôn ngữ:

$ grep --color='auto' -P -n "[^\x00-\x7F]" notes_unicode_emoji_test

$ perl -ne 'print "$. $_" if m/[\x00-\x08\x0E-\x1F\x80-\xFF]/' notes_unicode_emoji_test  

1 ‐‐ unicode dashes e28090
3 💘 Heart With Arrow Emoji - Emojipedia == UTF8? f09f9298
5 © copyright c2a9
7 call  underscore c2a0
9 CTRL-H CHARS URK URK URK 
11 LIVEE! あいうえお かが アイウエオ カガ   ซฌ आइ  YEOW, mix of japanese and chars from other e38182 e38184 . . e0a487
29 1 ‐‐ unicode dashes
30 3 💘 Heart With Arrow Emoji - Emojipedia == UTF8 e28090
31 5 © copyright
32 7 call  underscore
33 11 LIVEE! あいうえお かが アイウエオ カガ   ซฌ आइ  YEOW, mix of japanese and chars from other
34 52 LIVEE! あいうえお かが アイウエオ カガ   ซฌ आइ  YEOW, mix of japanese and chars from other
73 LIVEE! あいうえお かが アイウエオ カガ   ซฌ आइ  YEOW, mix of japanese and chars from other

VÌ các công cụ tìm char không phải ascii ưa thích:

$ perl -ne 'print "$. $_" if m/[\x00-\x08\x0E-\x1F\x80-\xFF]/' notes_unicode_emoji_test

như trong câu trả lời hàng đầu, grep ngược:

$ grep --color='auto' -P -n "[^\x00-\x7F]" notes_unicode_emoji_test

như trong câu trả lời hàng đầu nhưng VỚI LC_ALL=C:

$ LC_ALL=C grep --color='auto' -P -n "[\x80-\xFF]" notes_unicode_emoji_test

1
Trả lời lý do tại sao grep không khớp các ký tự được mã hóa trong hơn 2 byte nhờ @calandoa và frabjous trong các nhận xét ở trên về câu hỏi. Sử dụng LC_ALL = C trước lệnh grep.
gaoithe

1
Cảm ơn rất nhiều vì đã làm phiền để gửi một câu trả lời bị chôn vùi dưới 800 upvote khác! Vấn đề của tôi là một ký tự 0x02. Bạn có thể muốn đặt "ví dụ thực tế về sử dụng" ở gần đầu, vì bạn thực sự không cần phải đọc toàn bộ bài viết để xem đó có phải là vấn đề của bạn không.
Noumenon

1
Tôi biết, câu trả lời thực sự cũ, và chi tiết khó chịu, nhưng chính xác hữu ích cho tôi và những người khác tôi cũng hy vọng. Bạn nói đúng, tôi đã thêm TLDR; ở đầu.
gaoithe

1

Kỳ lạ thay, tôi đã phải làm điều này ngày hôm nay! Tôi đã kết thúc bằng Perl vì tôi không thể làm cho grep / egrep hoạt động (ngay cả ở chế độ -P). Cái gì đó như:

cat blah | perl -en '/\xCA\xFE\xBA\xBE/ && print "found"'

Đối với các ký tự unicode (như \u2212trong ví dụ dưới đây) sử dụng:

find . ... -exec perl -CA -e '$ARGV = @ARGV[0]; open IN, $ARGV; binmode(IN, ":utf8"); binmode(STDOUT, ":utf8"); while (<IN>) { next unless /\N{U+2212}/; print "$ARGV: $&: $_"; exit }' '{}' \;

1

Thật thú vị khi biết cách tìm kiếm một ký tự unicode. Lệnh này có thể giúp đỡ. Bạn chỉ cần biết mã trong UTF8

grep -v $'\u200d'

Tôi không thực sự là một chuyên gia, nhưng tôi biết đủ để biết đó không phải là đại diện UTF8, đó là UTF16, hoặc có thể là UTF32 hoặc UCS16. Đối với một mật mã 2 byte, cả ba đều có thể giống nhau.
Baxissimo

1

Tìm tất cả các ký tự không phải mã ascii cho cảm giác rằng một người đang tìm kiếm các chuỗi unicode hoặc có ý định tách từng ký tự đã nói.

Trước đây, hãy thử một trong số này (biến fileđược sử dụng để tự động hóa):

 file=file.txt ; LC_ALL=C grep -Piao '[\x80-\xFF\x20]{7,}' $file | iconv -f $(uchardet $file) -t utf-8

 file=file.txt ; pcregrep -iao '[\x80-\xFF\x20]{7,}' $file | iconv -f $(uchardet $file) -t utf-8

 file=file.txt ; pcregrep -iao '[^\x00-\x19\x21-\x7F]{7,}' $file | iconv -f $(uchardet $file) -t utf-8

Vanilla grep không hoạt động chính xác mà không có LC_ALL = C như đã lưu ý trong các câu trả lời trước.

Phạm vi ASCII là x00-x7F, không gian là x20, vì các chuỗi có khoảng trắng, phạm vi phủ định bỏ qua nó.

Phạm vi không phải ASCII là x80-xFF, vì các chuỗi có khoảng trắng, phạm vi dương sẽ thêm nó.

Chuỗi được cho là có ít nhất 7 ký tự liên tiếp trong phạm vi. {7,}.

Đối với đầu ra có thể đọc được, uchardet $filetrả về dự đoán mã hóa tệp được truyền cho iconv để nội suy tự động.


Điều này rất hữu ích do đề cập đến uchardetlệnh. Cảm ơn vì sự ủng hộ đó!
bballdave025
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.