GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Wie man in einem Bash-Skript nach Unicode sucht

grep ist das falsche Werkzeug für den Job.

Sie sehen die � U+FFFD REPLACEMENT CHARACTER nicht weil es buchstäblich im Dateiinhalt steht, sondern weil Sie sich eine Binärdatei mit einem Tool angesehen haben, das nur textbasierte Eingaben verarbeiten soll. Die Standardmethode zur Behandlung ungültiger Eingaben (d. h. zufälliger Binärdaten) besteht darin, alles, was in der aktuellen Locale (höchstwahrscheinlich UTF-8) nicht gültig ist, durch U+FFFD zu ersetzen, bevor es auf dem Bildschirm angezeigt wird.

Das heißt, es ist sehr wahrscheinlich, dass ein wörtlicher \xEF\xBF\xBD (die UTF-8-Bytesequenz für das U+FFFD-Zeichen) kommt nie in der Datei vor. grep völlig zu Recht, wenn er Ihnen sagt, dass es keinen gibt.

Eine Möglichkeit zu erkennen, ob eine Datei eine unbekannte Binärdatei enthält, ist der file(1) Befehl:

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

Für jeden unbekannten Dateityp wird einfach data angezeigt . Versuchen Sie es

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

um zu prüfen, ob die Datei wirklich beliebige Binärdateien und damit höchstwahrscheinlich Müll enthält.

Wenn Sie sicherstellen möchten, dass out.txt nur eine UTF-8-kodierte Textdatei ist, können Sie alternativ iconv verwenden :

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

TL;DR:

grep -axv '.*' out.txt 

lange Antwort

Beide vorliegenden Antworten sind äußerst irreführend und grundsätzlich falsch.

Holen Sie sich zum Testen diese beiden Dateien (von einem sehr angesehenen Entwickler:Markus Kuhn ):

$ 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

Demo

Die erste UTF-8-demo.txt ist eine Datei, die entwickelt wurde, um zu zeigen, wie gut UTF-8 viele Sprachen, Mathematik, Braille und viele andere nützliche Zeichentypen darstellen kann. Werfen Sie einen Blick mit einem Texteditor (der UTF-8 versteht) und Sie werden viele Beispiele sehen und nein .

Der Test, den eine Antwort vorschlägt:den Zeichenbereich auf \x00-\x7F zu begrenzen wird fast alles in dieser Datei ablehnen.
Das ist sehr falsch und wird kein entfernen da es keine in dieser Datei gibt .

Durch die Verwendung des in dieser Antwort empfohlenen Tests wird 72.5 % entfernt der Datei:

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

Das ist (für die meisten praktischen Zwecke) die gesamte Datei. Eine Datei, die sehr gut entworfen wurde, um vollkommen gültige Zeichen anzuzeigen.

Test

Die zweite Datei soll mehrere Grenzfälle ausprobieren, um zu bestätigen, dass utf-8-Lesegeräte gute Arbeit leisten. Es enthält viele Zeichen, die dazu führen, dass ein „�“ angezeigt wird. Aber die andere Antwortempfehlung (die ausgewählte) ist, file zu verwenden scheitert grob mit dieser Datei. Nur das Entfernen eines Nullbytes (\0 ) (was technisch gültiges ASCII ist) und ein \x7f byte (DEL - delete) (was eindeutig auch ein ASCII-Zeichen ist) ergibt all die für file gültige Datei Befehl:

$ 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

Nicht nur file die viele nicht erkennen falsche Zeichen, erkennt und meldet aber auch nicht, dass es sich um eine UTF-8-kodierte Datei handelt.

Und ja, file kann UTF-8-codierten Text erkennen und melden:

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

Auch file meldet die meisten Steuerzeichen im Bereich von 1 bis 31 nicht als ASCII. Es (file ) meldet einige Bereiche als data :

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

Andere als ASCII text :

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

Als druckbarer Zeichenbereich (mit Zeilenumbrüchen):

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

Einige Bereiche können jedoch zu seltsamen Ergebnissen führen:

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

Das Programm file ist kein Werkzeug zum Erkennen von Text, sondern zum Erkennen von Magie Nummern in ausführbaren Programmen oder Dateien.

Die Bereiche file erkennen, und der entsprechende gemeldete Typ, den ich gefunden habe, war:

  • Ein-Byte-Werte, meistens 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
    
  • Utf-8-kodierte Bereiche:

    {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
    

Eine mögliche Lösung finden Sie unten.

Vorherige Antwort.

Der Unicode-Wert für das von Ihnen gepostete Zeichen ist:

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

Ja, das ist ein Unicode-Zeichen 'REPLACEMENT CHARACTER' (U+FFFD). Das ist ein Zeichen, das verwendet wird, um ungültig zu ersetzen Im Text gefundenes Unicode-Zeichen. Es ist eine "visuelle Hilfe", kein echter Charakter. Um jede vollständige Zeile zu finden und aufzulisten, die ungültiges UNICODE enthält Zeichen verwenden:

grep -axv '.*' out.txt 

Wenn Sie jedoch nur feststellen möchten, ob ein Zeichen ungültig ist, verwenden Sie:

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

Wenn das Ergebnis 1 ist die Datei ist sauber, sonst ist sie Null 0 .

Wenn Sie gefragt haben:Wie finde ich den ? verwenden Sie dann Folgendes:

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

Oder wenn Ihr System korrekt UTF-8-Text verarbeitet, einfach:

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

Diese sehr frühe Antwort war für den ursprünglichen Beitrag, der lautete:

Wie man in einem Bash-Skript nach Unicode sucht

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

Grundsätzlich, wenn die Datei "out.txt" irgendwo in der Datei "�" enthält, möchte ich, dass es "funktioniert" wiedergibt UND wenn die Datei "out.txt" KEIN "�" irgendwo in der Datei enthält, dann würde ich es mögen zu cat out.txt

Versuchen Sie es mit

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

mit einem if .. then Anweisung wie folgt:

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

Erklärung:

  • -P , --perl-regexp :PATTERN ist ein regulärer Perl-Ausdruck
  • -o , --only-matching :zeigt nur den Teil einer Linie, der zu PATTERN passt
  • [^\x00-\x7F] ist ein regulärer Ausdruck, der einem einzelnen Nicht-ASCII-Zeichen entspricht.
  • [[:ascii:]] - entspricht einem einzelnen ASCII-Zeichen
  • [^[:ascii:]] - stimmt mit einem einzelnen Nicht-ASCII-Zeichen überein

in bash

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

Linux
  1. Wie überprüfe ich die Teilzeichenfolge in Shell Script Bash?

  2. Wie man den Alias-Befehl in einem Bash-Skript oder einer Bashrc-Datei zum Laufen bringt

  3. Wie kann ich in einer Datei nach einem mehrzeiligen Muster suchen?

  4. Woher weiß ich den Namen der Skriptdatei in einem Bash-Skript?

  5. Wie kann man Bash-Skripte in Vim hervorheben?

So lesen Sie eine Datei Zeile für Zeile in Bash

35 Bash-Skriptbeispiele

So führen Sie ein Bash-Skript aus

Wie suche ich nach Inhalten nach dem Muster?

Zeigen Sie Unicode-Codepunkte für alle Buchstaben in der Datei auf Bash an

Wie man einen Abschnitt einer Datei in der Bash-Shell grep