Jak mogę grepować dla wszystkich znaków spoza ASCII?


359

Mam kilka bardzo dużych plików XML i próbuję znaleźć wiersze zawierające znaki spoza ASCII. Próbowałem następujące:

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

Ale to zwraca każdą linię w pliku, niezależnie od tego, czy linia zawiera znak z określonego zakresu.

Czy mam niewłaściwą składnię, czy robię coś innego źle? Próbowałem też:

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

(z pojedynczymi i podwójnymi cudzysłowami otaczającymi wzór).


Znaki ASCII mają tylko jeden bajt, więc jeśli plik nie jest Unicode, nie powinno być żadnych znaków powyżej 0xFF.
zdav

Jak idziemy powyżej \ xFF? Grep wyświetla błąd „grep: poza zakresem w klasie znaków”.
Mudit Jain,

Odpowiedzi:


493

Możesz użyć polecenia:

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

To da ci numer linii i podświetli na czerwono znaki inne niż ascii.

W niektórych systemach, w zależności od ustawień, powyższe nie będzie działać, więc możesz grep odwrotnie

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

Zauważ też, że ważnym bitem jest -Pflaga, która jest równa --perl-regexp: więc zinterpretuje twój wzór jako wyrażenie regularne Perla. Mówi to również

jest to wysoce eksperymentalne i grep -P może ostrzegać przed niewdrożonymi funkcjami.


42
To nie będzie działać w BSD grep(w OS X 10.8 Mountain Lion), ponieważ nie obsługuje tej Popcji.
Bastiaan M. van de Weerd

20
Aby zaktualizować mój ostatni komentarz, wersja GNU grepjest dostępna w dupesbibliotece Homebrew (włącz używanie brew tap homebrew/dupes):brew install grep
Bastiaan M. van de Weerd

48
@ BastiaanVanDeWeerd jest poprawny, grep na OSX 10.8 nie obsługuje już PCRE („wyrażenia regularne zgodne z Perlem”), ponieważ Darwin używa teraz grep BSD zamiast GNU grep. Alternatywą do zainstalowania dupesbiblioteki jest instalacja pcrezamiast: brew install pcre... w ramach tego otrzymasz pcregrepnarzędzie, którego możesz użyć w następujący sposób:pcregrep --color='auto' -n "[\x80-\xFF]" file.xml
pvandenberk

15
brewUżytkownicy komputerów Mac mogą zainstalować jądra GNUbrew install coreutils . To da ci wiele narzędzi GNU z prefiksem „g” - w tym przypadku użyj ggrep. Powinno to uniknąć problemów wynikających z wymiany narzędzia systemowego, ponieważ specyficzne dla systemu skrypty Mac zależą teraz od grep BSD.
Joel Purra

22
działa to dobrze na ag "[\x80-\xFF]" filethe_silver_searcher
Macu,

123

Zamiast przyjmować założenia dotyczące zakresu bajtów znaków spoza ASCII, podobnie jak większość powyższych rozwiązań, nieco lepiej IMO wyraźnie określa rzeczywisty zakres bajtów znaków ASCII.

Tak więc pierwszym rozwiązaniem byłoby na przykład:

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

(który w zasadzie greps dla dowolnego znaku spoza zakresu szesnastkowego ASCII: od \ x00 do \ x7F)

Na Mountain Lion, który nie będzie działał (z powodu braku obsługi PCRE w BSD grep) , ale z pcrezainstalowanym przez Homebrew, będą działały równie dobrze:

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

Jakieś zalety lub wady, o których każdy może pomyśleć?


9
To faktycznie działało dla mnie, gdy powyższe rozwiązania zawiodły. Znalezienie apostrofów M $ Word nie było łatwiejsze!
AlbertEngelB

2
Jeśli masz powłokę kompatybilną z bash, ale nie działa pcre-grep, LC_COLLATE=C grep $'[^\1-\177]'działa (dla plików bez bajtów zerowych)
idupree

2
To rozwiązanie wydaje się działać bardziej konsekwentnie niż powyższe.
0xcaff,

1
Musiałem użyć tego do pobrania Kanji, cyrylicy i tradycyjnego chińskiego w moim pliku UTF8, używając „[\ x80- \ xFF]” pominąłem wszystkie te.
buckaroo1177125

1
Pro jest działał doskonale, podczas gdy inne opcje były świetne, ale nie tak świetne. Dotychczas nie znaleziono wad.
jwpfox,

67

Poniższe działa dla mnie:

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

Znaki inne niż ASCII zaczynają się od 0x80 i przechodzą do 0xFF, patrząc na bajty. Grep (i rodzina) nie przetwarzają Unicode, aby scalić znaki wielobajtowe w jedną całość w celu dopasowania wyrażenia regularnego, jak się wydaje. -POpcja w moim grep umożliwia korzystanie z \xdducieczek z klas znakowych aby osiągnąć to, co chcesz.


1
Aby wyświetlić widok, który może nie od razu wiedzieć, jak wywołać to w wielu plikach, wystarczy uruchomić: find. -nazwa * .xml | xargs grep -P „[\ x80- \ xFF]”
David Mohundro,

1
To zwraca dopasowanie, ale nic nie wskazuje na to, kim jest i gdzie jest postać. Jak widzimy, czym jest postać i gdzie ona jest?
Faheem Mitha,

Dodanie „-n” da numer linii, dodatkowo niewidoczne znaki wyświetlą się jako blok na terminalu: grep -n -P „[\ x80- \ xFF]” plik.xml
fooMonster

4
Mam problem z Hangul Korean: echo '소녀시대' | grep -P "[\x80-\xFF]"nic mi nie zwraca - czy ktoś może to potwierdzić? (GNU grep 2.21)
frabjous

@frabjous samo tutaj, ale grepping odwrotności prace: echo '소녀시대' | grep -P "[^\x00-\x7F]". Lub po prostu użyj, the_silver_searcherjak wskazał @slf:echo '소녀시대' | ag "[\x80-\xFF]"
psmith

55

W perl

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

1
Na OSX10.11 musiałem wypróbować kilka rozwiązań grep + regex, zanim znalazłem to, co faktycznie działa
sg

Chcesz udostępnić to rozwiązanie OSX @ sg ?!
geotheory

Powyższy skrypt perla to rozwiązanie, o którym mówię
sg

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

43

Prostym sposobem jest zdefiniowanie znaku spoza ASCII ... jako znaku, który nie jest znakiem ASCII.

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

Dodaj kartę po ^razie potrzeby.

Ustawienie LC_COLLATE=Cpozwala uniknąć przykrych niespodzianek dotyczących znaczenia zakresów postaci w wielu lokalizacjach. Ustawienie LC_CTYPE=Cjest konieczne, aby dopasować znaki jednobajtowe - w przeciwnym razie w poleceniu pominięte zostaną nieprawidłowe sekwencje bajtów w bieżącym kodowaniu. Ustawienie LC_ALL=Ccałkowicie eliminuje efekty zależne od lokalizacji.


Na RedHat 6.4 z tcsh musiałem użyć <<< env LC_COLLATE = C grep -n '[^ - ~]' file.xml >>>. Dodałem -n, aby uzyskać numer linii.
ddevienne

Dla mnie echo "A" | LC_COLLATE=C grep '[^ -~]'zwraca mecz
frabjous

1
@frabjous Jeśli masz LC_ALL=en_US.UTF-8, to przebija LC_COLLATEustawienie. Nie powinieneś mieć tego w swoim środowisku! LC_ALLpolega tylko na wymuszeniu określonego zadania, aby użyć określonego ustawienia narodowego, zwykle C. Aby ustawić domyślne ustawienia regionalne dla wszystkich kategorii, ustaw LANG.
Gilles „SO- przestań być zły”

1
Na początku nie dodałem LC_ALL=C, że działa inaczej w Mac OS X i Ubuntu. Po dodaniu tego ustawienia dają ten sam wynik.
Max Peng

1
Działa to na komputerze Mac, podczas gdy inne rozwiązania oparte na grep nie.
Matthias Fripp

26

Oto inny wariant, który znalazłem, który dał zupełnie inne wyniki niż wyszukiwanie grep [\x80-\xFF]w zaakceptowanej odpowiedzi. Być może przyda się ktoś, kto znajdzie dodatkowe postacie inne niż ascii:

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

Uwaga: grep mojego komputera (Mac) nie miał -Popcji, więc zrobiłem brew install grepi rozpocząłem powyższe połączenie z ggrepzamiast grep.


2
To zdecydowanie najlepsza odpowiedź, ponieważ działa zarówno na komputerach Mac, jak i Linux.
tommy.carstensen

Tylko ten, który pracował dla mnie w systemie Linux.

9

Działa następujący kod:

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

Zamień /tmpna nazwę katalogu, który chcesz przeszukać.


2
Na Macu to działa, podczas gdy większość z nich nie działa.
Matthias Fripp

9

Wyszukiwanie znaków niedrukowalnych. TLDR; Streszczenie

  1. szukaj znaków kontrolnych ORAZ rozszerzonego Unicode
  2. ustawienie regionalne np. LC_ALL=Cpotrzebne, aby grep działał zgodnie z oczekiwaniami z rozszerzonym Unicode

SO preferowane wyszukiwarki znaków innych niż ascii:

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

jak w górnej odpowiedzi, odwrotne grep:

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

jak w górnej odpowiedzi, ale Z LC_ALL=C:

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

. . więcej . . dręczące szczegóły na ten temat:. . .

Zgadzam się z Harveyem powyżej ukrytym w komentarzach, często bardziej przydatne jest wyszukiwanie znaków niedrukowalnych LUB łatwo jest myśleć bez ASCII, kiedy naprawdę powinieneś myśleć o drukowaniu. Harvey sugeruje „użyj tego:” [^\n -~]”. Dodaj \ r dla plików tekstowych DOS. To tłumaczy się na„ [^\x0A\x020-\x07E]”i dodaje \ x0D dla CR”

Ponadto dodanie -c (pokaż liczbę dopasowanych wzorców) do grep jest przydatne podczas wyszukiwania znaków niedrukowalnych, ponieważ dopasowane łańcuchy mogą zepsuć terminal.

Odkryłem, że dodanie zakresu 0-8 i 0x0e-0x1f (do zakresu 0x80-0xff) jest użytecznym wzorcem. Wyklucza to TAB, CR i LF oraz jeden lub dwa inne nietypowe znaki drukowalne. Więc IMHO jest dość przydatnym (choć surowym) wzorem grepa:

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

AKTUALNIE, ogólnie rzecz biorąc, musisz to zrobić:

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

awaria:

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

Np. Praktyczny przykład użycia find do grepowania wszystkich plików w bieżącym katalogu:

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

Czasami możesz chcieć dostosować grep. np. znak BS (0x08 - backspace) używany w niektórych plikach do wydruku lub w celu wykluczenia VT (0x0B - tabulator pionowy). Znaki BEL (0x07) i ESC (0x1B) można również uznać za drukowalne w niektórych przypadkach.

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

AKTUALIZACJA: Musiałem ostatnio to ponownie odwiedzić. I YYMV w zależności od ustawień terminala / słonecznej prognozy pogody ALE. . Zauważyłem, że grep nie znalazł wielu znaków Unicode lub Extended. Mimo że intuicyjnie powinny pasować do zakresu od 0x80 do 0xff, 3 i 4 bajtowe znaki Unicode nie zostały dopasowane. ??? Czy ktoś może to wyjaśnić? TAK. @frabjous zapytał, a @calandoa wyjaśnił, że LC_ALL=Cnależy użyć, aby ustawić ustawienia regionalne dla polecenia, aby dopasować grep.

np. moja lokalizacja jest LC_ALL=pusta

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

grep z LC_ALL=pustymi dopasowaniami 2-bajtowe znaki zakodowane, ale nie zakodowane 3 i 4 bajty:

$ 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 z LC_ALL=Cwydaje się pasować do wszystkich rozszerzonych znaków, które chciałbyś:

$ 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

TO dopasowanie perla (częściowo znalezione gdzie indziej przy przepływie stosu) LUB odwrotne grep na górnej odpowiedzi NIE wydają się znajdować WSZYSTKICH ~ dziwnych ~ i ~ cudownych ~ "nie-ascii" znaków bez ustawiania ustawień regionalnych:

$ 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

SO preferowane wyszukiwarki znaków innych niż ascii:

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

jak w górnej odpowiedzi, odwrotne grep:

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

jak w górnej odpowiedzi, ale Z LC_ALL=C:

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

1
Odpowiedz, dlaczego grep nie pasuje do znaków zakodowanych w więcej niż 2 bajtach dzięki @calandoa i frabjous w powyższych komentarzach do pytania. Użyj LC_ALL = C przed poleceniem grep.
gaoithe

1
Bardzo dziękuję za kłopot z opublikowaniem odpowiedzi zakopanej pod 800 innymi głosami! Mój problem to znak 0x02. Możesz umieścić ten „praktyczny przykład użycia” u góry, ponieważ tak naprawdę nie musisz czytać całego postu, aby zobaczyć, czy to jest twój problem.
Noumenon,

1
Wiem, naprawdę stara odpowiedź i sprawdzające szczegóły, ale mam nadzieję, że poprawne przydatne dla mnie i innych. Masz rację, dodałem TLDR; na szczycie.
gaoithe

1

O dziwo, musiałem to dziś zrobić! Skończyło się na użyciu Perla, ponieważ nie mogłem zmusić grep / egrep do pracy (nawet w trybie -P). Coś jak:

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

W przypadku znaków Unicode (jak \u2212w przykładzie poniżej) użyj tego:

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

Interesujące może być sprawdzenie, jak wyszukać jedną postać Unicode. To polecenie może pomóc. Musisz tylko znać kod w UTF8

grep -v $'\u200d'

Nie jestem ekspertem, ale wiem wystarczająco dużo, aby wiedzieć, że to nie jest reprezentacja UTF8, to UTF16, a może UTF32 lub UCS16. W przypadku 2-bajtowego punktu kodowego wszystkie trzy mogą być takie same.
Baxissimo,

1

Znalezienie wszystkich znaków innych niż ascii sprawia wrażenie, że albo szuka się ciągów Unicode, albo zamierza usunąć te znaki indywidualnie.

W przypadku tych pierwszych wypróbuj jedną z nich (zmienna filesłuży do automatyzacji):

 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

Waniliowe grep nie działa poprawnie bez LC_ALL = C, jak wspomniano w poprzednich odpowiedziach.

Zakres ASCII to x00-x7Fspacja x20, ponieważ ponieważ łańcuchy mają spacje, zakres ujemny pomija je.

Zakres inny niż ASCII jest taki x80-xFF, że ponieważ ciągi znaków mają spacje, zakres dodatni dodaje je.

Zakłada się, że ciąg znaków składa się z co najmniej 7 kolejnych znaków w zakresie. {7,}.

Dla danych wyjściowych odczytywanych przez powłokę uchardet $filezwraca domysły kodowania pliku, które jest przekazywane do iconv w celu automatycznej interpolacji.


Jest to bardzo przydatne ze względu na wzmiankę o uchardetpoleceniu. Dzięki za heads-up!
bballdave025
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.