GNU/Linux >> Znalost Linux >  >> Linux

Jak grep pro unicode � ve skriptu bash

grep je pro danou úlohu nesprávným nástrojem.

Zobrazí se � U+FFFD REPLACEMENT CHARACTER ne proto, že je to doslova v obsahu souboru, ale protože jste se podívali na binární soubor pomocí nástroje, který má zpracovávat pouze textový vstup. Standardní způsob, jak zpracovat neplatný vstup (tj. náhodná binární data), je nahradit vše, co není platné v aktuálním národním prostředí (pravděpodobně UTF-8), U+FFFD, než se objeví na obrazovce.

To znamená, že je velmi pravděpodobné, že doslovný \xEF\xBF\xBD (sekvence bajtů UTF-8 pro znak U+FFFD) se v souboru nikdy nevyskytuje. grep má úplnou pravdu, když vám říkám, že žádný neexistuje.

Jedním ze způsobů, jak zjistit, zda soubor obsahuje nějakou neznámou binární hodnotu, je file(1) příkaz:

$ head -c 100 /dev/urandom > rubbish.bin
$ file rubbish.bin
rubbish.bin: data

Pro jakýkoli neznámý typ souboru to jednoduše řekne data . Zkuste

$ file out.txt | grep '^out.txt: data$'

zkontrolovat, zda soubor skutečně obsahuje libovolný binární soubor a tedy s největší pravděpodobností odpad.

Pokud se chcete ujistit, že out.txt je pouze textový soubor s kódováním UTF-8, můžete alternativně použít iconv :

$ iconv -f utf-8 -t utf-16 out.txt >/dev/null

TL;DR:

grep -axv '.*' out.txt 

dlouhá odpověď

Obě současné odpovědi jsou extrémně zavádějící a v zásadě špatné.

Chcete-li otestovat, získejte tyto dva soubory (od velmi uznávaného vývojáře:Markuse Kuhna):

$ wget https://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-demo.txt
$ wget https://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt

Ukázka

První UTF-8-demo.txt je soubor navržený tak, aby ukázal, jak dobře dokáže UTF-8 prezentovat mnoho jazyků, matematiku, Braillovo písmo a mnoho dalších užitečných typů znaků. Podívejte se pomocí textového editoru (který rozumí utf-8) a uvidíte spoustu příkladů a ne .

Test, který navrhuje jedna odpověď:omezit rozsah znaků na \x00-\x7F odmítne téměř vše v tomto souboru.
To je velmi špatné a neodstraní žádné protože v tom souboru žádný není .

Pomocí testu doporučeného v této odpovědi odstraníte 72.5 % souboru:

$ grep -oP "[^\x00-\x7F]" UTF-8-demo.txt | tr -d '\n' | wc -c
10192
$ cat UTF-8-demo.txt | wc -c
14058

To je (pro většinu praktických účelů) celý soubor. Soubor velmi dobře navržený tak, aby zobrazoval dokonale platné znaky.

Test

Druhý soubor je navržen tak, aby vyzkoušel několik hraničních případů, aby se potvrdilo, že čtečky utf-8 odvádějí dobrou práci. Obsahuje mnoho znaků, které způsobí zobrazení '�'. Ale doporučení druhé odpovědi (vybrané) použít file s tímto souborem hrubě selže. Pouze odstranění nulového bajtu (\0 ) (což je technicky platné ASCII) a \x7f byte (DEL - delete) (což je jasně také znak ASCII) vytvoří vše soubor platný pro file příkaz:

$ cat UTF-8-test.txt | tr -d '\0\177' > a.txt
$ file a.txt 
a.txt: Non-ISO extended-ASCII text, with LF, NEL line terminators

Nejen file nepodařilo zjistit mnoho nesprávné znaky, ale také nedokáže detekovat a nahlásit, že se jedná o soubor s kódováním UTF-8.

A ano, file je schopen detekovat a hlásit text kódovaný UTF-8:

$ echo "ééakjfhhjhfakjfhfhaéá" | file -
/dev/stdin: UTF-8 Unicode text

Také file nehlásí jako ASCII většinu řídicích znaků v rozsahu 1 až 31. It (file ) uvádí některé rozsahy jako data :

$ printf '%b' "$(printf '\\U%x' {1..6})" | file -
/dev/stdin: data

Ostatní jako ASCII text :

$ printf '%b' "$(printf '\\U%x' 7 {9..12})" | file -
/dev/stdin: ASCII text

Jako rozsah tisknutelných znaků (s novými řádky):

$ printf '%b' "$(printf '\\U%x' {32..126} 10)" | file -
/dev/stdin: ASCII text

Některé rozsahy však mohou způsobit podivné výsledky:

$ printf '%b' "$(printf '\\U%x' {14..26})" | file -
/dev/stdin: Atari MSA archive data, 4113 sectors per track, starting track: 5141, ending track: 5655

Program file není nástroj k detekci textu, ale k detekci kouzla čísla ve spustitelných programech nebo souborech.

Rozsahy file detekovat a odpovídající typ, který jsem našel, byl:

  • Jednobajtové hodnoty, většinou ascii:

    {1..6} {14..26} {28..31} 127   :data
    {128..132} {134..159}          :Non-ISO extended-ASCII text
    133                            :ASCII text, with LF, NEL line terminators
    27                             :ASCII text, with escape sequences
    13                             :ASCII text, with CR, LF line terminators
    8                              :ASCII text, with overstriking
    7 {9..12} {32..126}            :ASCII text
    {160..255}                     :ISO-8859 text
    
  • Rozsahy kódované Utf-8:

    {1..6} {14..26} {28..31} 127   :data
    27                             :ASCII text, with escape sequences
    13                             :ASCII text, with CR, LF line terminators
    8                              :ASCII text, with overstriking
    7 {9..12} {32..126}            :ASCII text
    {128..132} {134..159}          :UTF-8 Unicode text
    133                            :UTF-8 Unicode text, with LF, NEL line terminators
    {160..255}                     :UTF-8 Unicode text
    {256..5120}                    :UTF-8 Unicode text
    

Jedno z možných řešení je uvedeno níže.

Předchozí odpověď.

Hodnota Unicode pro znak, který posíláte, je:

$ printf '%x\n' "'�"
fffd

Ano, to je znak Unicode 'NÁHRADNÍ ZNAK' (U+FFFD). To je znak používaný k nahrazení jakéhokoli neplatného V textu byl nalezen znak Unicode. Je to „vizuální pomůcka“, nikoli skutečná postava. Chcete-li najít a vypsat každý celý řádek, který obsahuje neplatný kód UNICODE znaky používají:

grep -axv '.*' out.txt 

ale pokud chcete pouze zjistit, zda je některý znak neplatný, použijte:

grep -qaxv '.*' out.txt; echo $?

Pokud je výsledek 1 soubor je čistý, jinak bude nula 0 .

Pokud jste se ptali:jak najít znak, pak použijte toto:

➤ a='Basically, if the file "out.txt" contains "�" anywhere in the file I'
➤ echo "$a" | grep -oP $(printf %b \\Ufffd)
�

Nebo pokud váš systém správně zpracovává text UTF-8, jednoduše:

➤ echo "$a" | grep -oP '�'
�

Tato velmi časná odpověď byla pro původní příspěvek, který byl:

Jak grep pro unicode � ve skriptu bash

if grep -q "�" out.txt
    then
        echo "working"
    else
        cat out.txt  fi

V zásadě, pokud soubor „out.txt“ obsahuje „�“ kdekoli v souboru, chtěl bych, aby se opakoval „working“ A pokud soubor „out.txt“ nikde v souboru NEobsahuje „�“, pak bych to chtěl to cat out.txt

Zkuste

grep -oP "[^\x00-\x7F]"

s if .. then prohlášení takto:

if grep -oP "[^\x00-\x7F]" file.txt; then
    echo "grep found something ..."
else
    echo "Nothing found!"
fi

Vysvětlení:

  • -P , --perl-regexp :PATTERN je regulární výraz jazyka Perl
  • -o , --only-matching :zobrazí pouze část řádku odpovídající PATTERN
  • [^\x00-\x7F] je regulární výraz, který odpovídá jedinému znaku mimo ASCII.
  • [[:ascii:]] - odpovídá jednomu ASCII znaku
  • [^[:ascii:]] - odpovídá jednomu ne-ASCII znaku

v bash

LC_COLLATE=C grep -o '[^ -~]' file

Linux
  1. Může být Bash skript připojen k souboru?

  2. Jak zkontrolovat podřetězec v Shell Script Bash?

  3. Jak rozdělit řetězec ve skriptu Bash

  1. Jak zajistit, aby příkaz alias fungoval ve skriptu bash nebo souboru bashrc

  2. Jak mohu vyhledat víceřádkový vzor v souboru?

  3. Jak analyzovat soubor CSV v Bash?

  1. Jak 'grep' nepřetržitý proud?

  2. Jak zahrnout soubor do skriptu bash shell

  3. Jak grepovat část souboru v bash shellu