GNU/Linux >> Linux Esercitazione >  >> Linux

Come grep per unicode � in uno script bash

grep è lo strumento sbagliato per il lavoro.

Vedrai � U+FFFD REPLACEMENT CHARACTER non perché è letteralmente nel contenuto del file, ma perché hai guardato un file binario con uno strumento che dovrebbe gestire solo l'input basato su testo. Il modo standard per gestire input non validi (ovvero dati binari casuali) è sostituire tutto ciò che non è valido nella locale corrente (molto probabilmente UTF-8) con U+FFFD prima che venga visualizzato sullo schermo.

Ciò significa che è molto probabile che un \xEF\xBF\xBD letterale (la sequenza di byte UTF-8 per il carattere U+FFFD) non compare mai nel file. grep ha perfettamente ragione a dirti che non ce n'è.

Un modo per rilevare se un file contiene qualche binario sconosciuto è con il file(1) comando:

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

Per qualsiasi tipo di file sconosciuto dirà semplicemente data . Prova

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

per verificare se il file contiene davvero binari arbitrari e quindi molto probabilmente spazzatura.

Se vuoi assicurarti che out.txt è solo un file di testo con codifica UTF-8, in alternativa puoi usare iconv :

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

TL;DR:

grep -axv '.*' out.txt 

risposta lunga

Entrambe le risposte attuali sono estremamente fuorvianti e fondamentalmente sbagliate.

Per testare, prendi questi due file (da uno sviluppatore molto apprezzato: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

Il primo UTF-8-demo.txt è un file progettato per mostrare quanto bene UTF-8 sia in grado di presentare molte lingue, matematica, braille e molti altri utili tipi di caratteri. Dai un'occhiata con un editor di testo (che capisce utf-8) e vedrai molti esempi e no .

Il test proposto da una risposta:limitare l'intervallo di caratteri a \x00-\x7F rifiuterà quasi tutto all'interno di questo file.
Questo è molto sbagliato e non rimuoverà alcun poiché non ce n'è nessuno in quel file .

L'utilizzo del test consigliato in quella risposta rimuoverà 72.5 % del file:

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

Questo è (per la maggior parte degli scopi pratici) l'intero file. Un file molto ben progettato per mostrare caratteri perfettamente validi.

Prova

Il secondo file è progettato per provare diversi casi limite per confermare che i lettori utf-8 stanno facendo un buon lavoro. Contiene al suo interno molti caratteri che faranno apparire un '�'. Ma l'altra raccomandazione di risposta (quella selezionata) per utilizzare file fallisce gravemente con questo file. Solo rimuovendo un byte zero (\0 ) (che tecnicamente è un ASCII valido) e un \x7f byte (DEL - delete) (che è chiaramente anche un carattere ASCII) renderà all il file valido per il file comando:

$ 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 solo file non riescono a rilevare i molti caratteri errati, ma non riesce nemmeno a rilevare e segnalare che si tratta di un file con codifica UTF-8.

E sì, file è in grado di rilevare e segnalare il testo con codifica UTF-8:

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

Inoltre, file non riesce a riportare come ASCII la maggior parte dei caratteri di controllo nell'intervallo da 1 a 31. It (file ) riporta alcuni intervalli come data :

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

Altri come ASCII text :

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

Come intervallo di caratteri stampabili (con newline):

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

Ma alcuni intervalli possono causare risultati strani:

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

Il programma file non è uno strumento per rilevare il testo, ma per rilevare la magia numeri in programmi o file eseguibili.

Gli intervalli file detect, e il tipo corrispondente riportato che ho trovato era:

  • Valori di un byte, principalmente 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
    
  • Intervalli con codifica 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
    

Una possibile soluzione si trova sotto.

Risposta precedente.

Il valore Unicode per il carattere che stai postando è:

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

Sì, questo è un carattere Unicode 'CARATTERE SOSTITUTIVO' (U+FFFD). Questo è un carattere utilizzato per sostituire qualsiasi non valido Carattere Unicode trovato nel testo. È un "ausilio visivo", non un vero e proprio personaggio. Per trovare ed elencare ogni riga completa che contiene UNICODE non valido i caratteri usano:

grep -axv '.*' out.txt 

ma se vuoi solo rilevare se qualche carattere non è valido, usa:

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

Se il risultato è 1 il file è pulito, altrimenti sarà zero 0 .

Se quello che stavi chiedendo era:come trovare il carattere, quindi, usa questo:

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

Oppure, se il tuo sistema elabora correttamente il testo UTF-8, semplicemente:

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

Questa primissima risposta era per il post originale che era:

Come grep per unicode � in uno script bash

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

Fondamentalmente, se il file "out.txt" contiene "�" in qualsiasi punto del file, vorrei che echo "funzionante" E se il file "out.txt" NON contiene "�" in qualsiasi punto del file, allora mi piacerebbe to cat out.txt

Prova

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

con un if .. then dichiarazione come segue:

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

Spiegazione:

  • -P , --perl-regexp :PATTERN è un'espressione regolare Perl
  • -o , --only-matching :mostra solo la parte di una linea corrispondente a PATTERN
  • [^\x00-\x7F] è una regex che corrisponde a un singolo carattere non ASCII.
  • [[:ascii:]] - corrisponde a un singolo carattere ASCII
  • [^[:ascii:]] - corrisponde a un singolo carattere non ASCII

in bash

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

Linux
  1. Come eseguire il debug di uno script Bash?

  2. Come far funzionare il comando alias nello script bash o nel file bashrc

  3. Come posso cercare uno schema multilinea in un file?

  4. Come faccio a conoscere il nome del file di script in uno script Bash?

  5. Come evidenziare gli script Bash in Vim?

Come leggere un file riga per riga in Bash

35 Esempi di script Bash

Come eseguire uno script Bash

Come includere un file in uno script di shell bash

Come grep \n nel file

Visualizza i punti di codice unicode per tutte le lettere nel file su bash