Как я grep для всех не-ASCII символов?


359

У меня есть несколько очень больших файлов XML, и я пытаюсь найти строки, которые содержат символы не ASCII. Я пробовал следующее:

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

Но это возвращает каждую строку в файле, независимо от того, содержит ли строка символ в указанном диапазоне.

У меня неправильный синтаксис или я делаю что-то еще неправильно? Я также попробовал:

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

(с одинарными и двойными кавычками вокруг шаблона).


Символы ASCII имеют длину только один байт, поэтому, если файл не является юникодом, не должно быть символов выше 0xFF.
здав

Как мы пойдем выше \ xFF? Grep выдает ошибку «grep: диапазон не по порядку в классе символов».
Mudit Jain

Ответы:


495

Вы можете использовать команду:

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

Это даст вам номер строки и выделит символы не-ascii красным цветом.

В некоторых системах, в зависимости от ваших настроек, вышеприведенное не сработает, поэтому вы можете использовать grep по обратной

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

Также обратите внимание, что важным битом является -Pфлаг, который соответствует --perl-regexp: поэтому он будет интерпретировать ваш шаблон как регулярное выражение Perl. Это также говорит о том, что

это очень экспериментально, и grep -P может предупредить о невыполненных функциях.


42
Это не будет работать в BSD grep(на OS X 10.8 Mountain Lion), так как он не поддерживает эту Pопцию.
Бастиан М. ван де Вирд

20
Чтобы обновить мой последний комментарий, версия GNU grepдоступна в dupesбиблиотеке Homebrew (включите использование brew tap homebrew/dupes):brew install grep
Бастиан М. ван де Веерд

48
@BastiaanVanDeWeerd правильно, grep в OSX 10.8 больше не поддерживает PCRE («Perl-совместимые регулярные выражения»), поскольку Дарвин теперь использует BSD grep вместо GNU grep. Альтернативой установке dupesбиблиотеки является установка pcreвместо: brew install pcre... как часть этого, вы получите pcregrepутилиту, которую вы можете использовать следующим образом:pcregrep --color='auto' -n "[\x80-\xFF]" file.xml
pvandenberk

15
Для brewпользователей Mac можно установить coreutils из GNUbrew install coreutils . Это даст вам много инструментов GNU с префиксом «g» - в этом случае используйте ggrep. Это должно избежать проблем, возникающих при замене системной утилиты, поскольку системные скрипты Mac теперь зависят от BSD grep.
Джоэл Пурра

22
это отлично работает на Mac, ag "[\x80-\xFF]" fileвам просто нужно установитьthe_silver_searcher
slf

123

Вместо того, чтобы делать предположения о диапазоне байтов не-ASCII-символов, как это делает большинство вышеупомянутых решений, немного лучше, чтобы IMO явно указывал фактический диапазон байтов ASCII-символов.

Таким образом, первое решение, например, станет:

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

(который в основном greps для любого символа вне шестнадцатеричного диапазона ASCII: от \ x00 до \ x7F)

На Mountain Lion это не будет работать (из-за отсутствия поддержки PCRE в BSD grep) , но с pcreустановленной через Homebrew, следующее будет работать также:

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

Есть плюсы или минусы, которые кто-нибудь может придумать?


9
Это на самом деле сработало для меня, где вышеперечисленные решения не удалось. Найти апострофы M $ Word еще проще!
AlbertEngelB

2
Если у вас есть bash-совместимая оболочка, но не работает pcre-grep, она LC_COLLATE=C grep $'[^\1-\177]'работает (для файлов без нулевых байтов)
idupree

2
Это решение работает более последовательно, чем приведенные выше.
0xcaff

1
Мне пришлось использовать это, чтобы забрать кандзи, кириллицу и традиционный китайский в моем файле UTF8, используя «[\ x80- \ xFF]», пропустив все это.
buckaroo1177125

1
Профи это работало превосходно, в то время как другие варианты были великолепны, но не так хороши. Никаких минусов пока не найдено.
jwpfox 19.09.16

67

Следующие работы для меня:

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

Не-ASCII символы начинаются с 0x80 и переходят в 0xFF при просмотре байтов. Grep (и семейство) не выполняют обработку Unicode, чтобы объединить многобайтовые символы в одну сущность для соответствия регулярному выражению, как вам кажется. -PВариант в моем Grep позволяет использовать \xddпобеги в классах символов , чтобы выполнить то , что вы хотите.


1
Для представления, которое может не сразу знать, как вызвать это для нескольких файлов, просто запустите: find. имя * .xml | xargs grep -P "[\ x80- \ xFF]"
Дэвид Мохундро

1
Это возвращает совпадение, но нет никаких указаний на то, что это за персонаж и где он находится. Как увидеть, что это за персонаж и где он?
Фахим Митха

Добавление «-n» даст номер строки, кроме того, невидимые символы будут отображаться в виде блока в терминале: grep -n -P «[\ x80- \ xFF]» file.xml
fooMonster

4
У меня проблема с Hangul Korean: echo '소녀시대' | grep -P "[\x80-\xFF]"для меня ничего не возвращается - кто-нибудь еще может подтвердить? (GNU grep 2.21)
безумно

@frabjous же здесь, но обратное оглавление работы: echo '소녀시대' | grep -P "[^\x00-\x7F]". Или просто используйте the_silver_searcherкак указано @slf:echo '소녀시대' | ag "[\x80-\xFF]"
Псмит

55

В перл

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

1
На OSX10.11 мне пришлось попробовать несколько решений grep + regex, прежде чем я нашел это, которое действительно работает
sg

Хотите поделиться этим OSX решением @sg ?!
геоэтерия

Приведенный выше скрипт на Perl - это решение, о котором я говорю
sg

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

43

Самый простой способ - определить не-ASCII-символ ... как символ, не являющийся ASCII-символом.

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

Добавьте вкладку после ^ если это необходимо.

Параметр LC_COLLATE=Cпозволяет избежать неприятных сюрпризов о значении диапазонов символов во многих локалях. Установка LC_CTYPE=Cнеобходима для соответствия однобайтовых символов - в противном случае команда пропустит недопустимые последовательности байтов в текущей кодировке. Настройка полностью LC_ALL=Cисключает зависящие от локали эффекты.


На RedHat 6.4 с tcsh мне пришлось использовать <<< env LC_COLLATE = C grep -n '[^ - ~]' file.xml >>>. Я добавил -n, чтобы получить номер строки.
ddevienne

Для меня echo "A" | LC_COLLATE=C grep '[^ -~]'возвращается матч
frabjous

1
@frabjous Если у вас есть LC_ALL=en_US.UTF-8, это превосходит LC_COLLATEнастройки. Вы не должны иметь это в своей среде! LC_ALLобычно заставляет определенную задачу использовать конкретную локаль C. Чтобы установить язык по умолчанию для всех категорий, установите LANG.
Жиль "ТАК - перестань быть злым"

1
Сначала я не добавлял LC_ALL=C, это ведет себя по-разному в Mac OS X и Ubuntu. После того, как я добавлю этот параметр, они дают тот же результат.
Макс Пэн

1
Это работает на Mac, а другие решения на основе grep - нет.
Матиас Фрипп

26

Вот еще один вариант, который я обнаружил, который дал совершенно разные результаты поиска grep [\x80-\xFF]в принятом ответе. Возможно, кому-то будет полезно найти дополнительные символы, не относящиеся к ascii:

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

Примечание: у grep моего компьютера (Mac) не было -Pопции, поэтому я сделал brew install grepи начал вызов выше с ggrepвместо grep.


2
Это, безусловно, лучший ответ, поскольку он работает как для Mac, так и для Linux.
tommy.carstensen

Только тот, который работал для меня на Linux.

9

Следующий код работает:

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

Замените /tmpна имя каталога, который вы хотите найти.


2
На Mac это работает, а большинство основанных на grep - нет.
Матиас Фрипп

9

Поиск непечатаемых символов. TLDR; Управляющее резюме

  1. поиск контрольных символов и расширенного Unicode
  2. настройка локали, например, LC_ALL=Cнеобходимая, чтобы grep делал то, что вы могли ожидать с расширенным юникодом

Так что предпочтительные не ASCII Char Finders:

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

как в верхнем ответе, обратный grep:

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

как в верхнем ответе, но с LC_ALL=C:

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

, , Больше . , Мучительная деталь в этом: , ,

Я согласен с Харви выше, погруженным в комментарии, часто полезнее искать непечатные символы ИЛИ легко думать не ASCII, когда вы действительно должны думать, что вы не печатаете. Харви предлагает "использовать это:" [^\n -~]". Добавьте \ r для текстовых файлов DOS. Это означает" [^\x0A\x020-\x07E]"и добавьте \ x0D для CR"

Кроме того, добавление -c (показать количество совпавших шаблонов) в grep полезно при поиске непечатаемых символов, поскольку соответствующие строки могут испортить терминал.

Я обнаружил, что добавление диапазона 0-8 и 0x0e-0x1f (к диапазону 0x80-0xff) является полезным шаблоном. Это исключает TAB, CR и LF и один или два необычных печатных символа. ИМХО, довольно полезный (хотя и грубый) шаблон grep это:

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

В действительности, как правило, вам нужно сделать это:

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

сломать:

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

Например, практический пример использования find для поиска всех файлов в текущем каталоге:

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

Вы можете настроить grep время от времени. например, BS (0x08 - backspace) char, используемый в некоторых печатаемых файлах или для исключения VT (0x0B - вертикальная вкладка). Символы BEL (0x07) и ESC (0x1B) также могут считаться пригодными для печати в некоторых случаях.

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

ОБНОВЛЕНИЕ: я должен был вернуться к этому недавно. И, YYMV в зависимости от настроек терминала / прогноза солнечной погоды, НО. , Я заметил, что grep не находил много юникодов или расширенных символов. Хотя интуитивно они должны соответствовать диапазону от 0x80 до 0xff, 3 и 4-байтовые символы Unicode не были сопоставлены. ??? Кто-нибудь может объяснить это? ДА. @frabjous спросил и @calandoa объяснил, что LC_ALL=Cследует использовать, чтобы установить языковой стандарт для команды, чтобы сделать совпадение grep.

например, мой язык LC_ALL=пуст

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

grep с LC_ALL=пустыми совпадениями 2-байтовые кодированные символы, но не 3 и 4-байтовые:

$ 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 с LC_ALL=Cдействительно соответствует всем расширенным символам, которые вы хотели бы:

$ 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

НАСТОЯЩЕЕ соответствие perl (частично найденное в другом месте в stackoverflow) ИЛИ обратный grep в верхнем ответе, похоже, обнаруживает ВСЕ странные ~ и ~ чудесные ~ "не-ascii" символы без установки языкового стандарта:

$ 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

Так что предпочтительные не ASCII Char Finders:

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

как в верхнем ответе, обратный grep:

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

как в верхнем ответе, но с LC_ALL=C:

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

1
Ответьте, почему grep не соответствует символам, закодированным более чем в 2 байтах, благодаря @calandoa и frabjous в комментариях выше к вопросу. Используйте LC_ALL = C перед командой grep.
gaoithe

1
Большое спасибо за то, что потрудились опубликовать ответ, похороненный под 800 другими возражениями! Моя проблема была 0x02 символ. Возможно, вы захотите поместить этот «практический пример использования» в самый верх, так как вам действительно не нужно читать весь пост, чтобы просто увидеть, является ли это вашей проблемой.
Ноумен

1
Я знаю, действительно старый ответ, и мучительные детали, но исправить, полезно для меня и других, также я надеюсь. Вы правы, я добавил TLDR; наверху
Гаоита

1

Странно, я должен был сделать это сегодня! Я закончил тем, что использовал Perl, потому что я не мог заставить работать grep / egrep (даже в режиме -P). Что-то вроде:

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

Для символов Юникода (например, \u2212 в примере ниже) используйте это:

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

Было бы интересно узнать, как искать один символ Юникода. Эта команда может помочь. Вам нужно только знать код в UTF8

grep -v $'\u200d'

Я на самом деле не эксперт, но я знаю достаточно, чтобы понять, что это не представление UTF8, это UTF16, или, может быть, UTF32 или UCS16. Для двухбайтовой кодовой точки эти три могут быть одинаковыми.
Baxissimo

1

Поиск всех символов, отличных от ascii, создает впечатление, что кто-то ищет строки в юникоде или намеревается раздеть эти символы по отдельности.

Для первого, попробуйте один из них (переменная fileиспользуется для автоматизации):

 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

Ванильный grep не работает правильно без LC_ALL = C, как отмечалось в предыдущих ответах.

Диапазон ASCII есть x00-x7F, пространствоx20 , так как в строках есть пробелы, которые отрицательный диапазон пропускает.

Диапазон не ASCII x80-xFF, так как строки имеют пробелы, положительный диапазон добавляет его.

Предполагается, что строка должна содержать не менее 7 последовательных символов в диапазоне. {7,},

Для вывода, читаемого оболочкой, uchardet $fileвозвращает предположение о кодировке файла, которая передается в iconv для автоматической интерполяции.


Это очень полезно из-за упоминания uchardetкоманды. Спасибо за это хедз-ап!
bballdave025
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.