GNU/Linux >> Tutoriels Linux >  >> Linux

Comment grep pour unicode � dans un script bash

grep est le mauvais outil pour le travail.

Vous voyez le � U+FFFD REPLACEMENT CHARACTER non pas parce que c'est littéralement dans le contenu du fichier, mais parce que vous avez regardé un fichier binaire avec un outil censé gérer uniquement les entrées textuelles. La manière standard de gérer les entrées invalides (c'est-à-dire les données binaires aléatoires) consiste à remplacer tout ce qui n'est pas valide dans les paramètres régionaux actuels (très probablement UTF-8) par U+FFFD avant qu'il n'apparaisse à l'écran.

Cela signifie qu'il est très probable qu'un \xEF\xBF\xBD littéral (la séquence d'octets UTF-8 pour le caractère U+FFFD) n'apparaît jamais dans le fichier. grep a tout à fait raison de vous dire qu'il n'y en a pas.

Une façon de détecter si un fichier contient un binaire inconnu est avec le file(1) commande :

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

Pour tout type de fichier inconnu, il dira simplement data . Essayez

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

pour vérifier si le fichier contient vraiment des binaires arbitraires et donc probablement des ordures.

Si vous voulez vous assurer que out.txt est un fichier texte encodé en UTF-8 uniquement, vous pouvez également utiliser iconv :

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

TL; DR :

grep -axv '.*' out.txt 

réponse longue

Les deux réponses actuelles sont extrêmement trompeuses et fondamentalement fausses.

Pour tester, récupérez ces deux fichiers (d'un développeur très réputé :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

Démo

Le premier UTF-8-demo.txt est un fichier conçu pour montrer à quel point UTF-8 est capable de présenter de nombreuses langues, mathématiques, braille et de nombreux autres types de caractères utiles. Jetez un œil avec un éditeur de texte (qui comprend utf-8) et vous verrez beaucoup d'exemples et non .

Le test proposé par une réponse :limiter la plage de caractères à \x00-\x7F rejettera presque tout à l'intérieur de ce fichier.
C'est très faux et ne supprimera aucun car il n'y en a pas dans ce fichier .

L'utilisation du test recommandé dans cette réponse supprimera 72.5 % du fichier :

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

C'est (à des fins pratiques) l'ensemble du fichier. Un fichier très bien conçu pour afficher des caractères parfaitement valides.

Tester

Le deuxième fichier est conçu pour essayer plusieurs cas limites afin de confirmer que les lecteurs utf-8 font du bon travail. Il contient à l'intérieur de nombreux caractères qui provoqueront l'affichage d'un '�'. Mais l'autre recommandation de réponse (celle sélectionnée) d'utiliser file échoue grossièrement avec ce fichier. Ne supprimer qu'un octet nul (\0 ) (qui est techniquement un ASCII valide) et un \x7f byte (DEL - delete) (qui est aussi clairement un caractère ASCII) fera tout le fichier valide pour le file commande :

$ 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

Non seulement file échouer à détecter les nombreux caractères incorrects, mais échoue également à détecter et à signaler qu'il s'agit d'un fichier encodé en UTF-8.

Et oui, file est capable de détecter et de signaler du texte encodé en UTF-8 :

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

Aussi, file ne signale pas comme ASCII la plupart des caractères de contrôle dans la plage 1 à 31. Il (file ) signale certaines plages comme data :

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

Autres comme ASCII text :

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

Comme plage de caractères imprimables (avec nouvelles lignes) :

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

Mais certaines plages peuvent provoquer des résultats étranges :

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

Le programme file n'est pas un outil pour détecter du texte, mais pour détecter la magie nombres dans les programmes ou fichiers exécutables.

Les plages file détecter, et le type correspondant signalé que j'ai trouvé était :

  • Valeurs d'un octet, principalement 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
    
  • Plages encodées en 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
    

Une solution possible se trouve ci-dessous.

Réponse précédente.

La valeur Unicode du caractère que vous publiez est :

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

Oui, c'est un caractère Unicode 'CARACTÈRE DE REMPLACEMENT' (U+FFFD). C'est un caractère utilisé pour remplacer tout invalide Caractère Unicode trouvé dans le texte. C'est une "aide visuelle", pas un vrai personnage. Pour rechercher et répertorier toutes les lignes complètes contenant un UNICODE non valide caractères utilisés :

grep -axv '.*' out.txt 

mais si vous voulez seulement détecter si un caractère est invalide, utilisez :

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

Si le résultat est 1 le fichier est propre, sinon il sera nul 0 .

Si ce que vous demandiez était :comment trouver le caractère, alors, utilisez ceci :

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

Ou si votre système traite correctement le texte UTF-8, simplement :

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

Cette toute première réponse concernait le message d'origine :

Comment grep pour unicode � dans un script bash

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

Fondamentalement, si le fichier "out.txt" contient "�" n'importe où dans le fichier, j'aimerais qu'il fasse écho à "working" ET si le fichier "out.txt" ne contient PAS "�" n'importe où dans le fichier alors je l'aimerais to cat out.txt

Essayez

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

avec un if .. then déclaration comme suit :

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

Explication :

  • -P , --perl-regexp :MOTIF est une expression régulière Perl
  • -o , --only-matching :affiche uniquement la partie d'une ligne correspondant à MOTIF
  • [^\x00-\x7F] est une expression régulière correspondant à un seul caractère non ASCII.
  • [[:ascii:]] - correspond à un seul caractère ASCII
  • [^[:ascii:]] - correspond à un seul caractère non-ASCII

en bash

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

Linux
  1. Comment déboguer un script bash ?

  2. Comment vérifier la sous-chaîne dans Shell Script Bash?

  3. Comment diviser une chaîne dans un script bash

  4. Comment puis-je rechercher un motif multiligne dans un fichier ?

  5. Comment connaître le nom du fichier de script dans un script Bash ?

Comment lire un fichier ligne par ligne dans Bash

35 exemples de scripts bash

Comment exécuter un script bash

Comment inclure un fichier dans un script shell bash

Comment grep \n dans le fichier

Afficher les points de code unicode pour toutes les lettres du fichier sur bash