grep
es la herramienta incorrecta para el trabajo.
Ves el � U+FFFD REPLACEMENT CHARACTER
no porque esté literalmente en el contenido del archivo, sino porque miró un archivo binario con una herramienta que se supone que solo maneja entradas basadas en texto. La forma estándar de manejar entradas no válidas (es decir, datos binarios aleatorios) es reemplazar todo lo que no es válido en la configuración regional actual (probablemente UTF-8) con U+FFFD antes de que llegue a la pantalla.
Eso significa que es muy probable que un literal \xEF\xBF\xBD
(la secuencia de bytes UTF-8 para el carácter U+FFFD) nunca aparece en el archivo. grep
tiene toda la razón al decirte que no hay ninguno.
Una forma de detectar si un archivo contiene algún binario desconocido es con el file(1)
comando:
$ head -c 100 /dev/urandom > rubbish.bin
$ file rubbish.bin
rubbish.bin: data
Para cualquier tipo de archivo desconocido, simplemente dirá data
. Prueba
$ file out.txt | grep '^out.txt: data$'
para verificar si el archivo realmente contiene algún binario arbitrario y, por lo tanto, probablemente basura.
Si quiere asegurarse de que out.txt
es solo un archivo de texto codificado en UTF-8, también puede usar iconv
:
$ iconv -f utf-8 -t utf-16 out.txt >/dev/null
TL;DR:
grep -axv '.*' out.txt
respuesta larga
Ambas respuestas actuales son extremadamente engañosas y básicamente incorrectas.
Para probar, obtenga estos dos archivos (de un desarrollador muy respetado: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
Demostración
El primer UTF-8-demo.txt
es un archivo diseñado para mostrar lo bien que UTF-8 es capaz de presentar muchos idiomas, matemáticas, braille y muchos otros tipos de caracteres útiles. Eche un vistazo con un editor de texto (que entienda utf-8) y verá muchos ejemplos y no �
.
La prueba que propone una respuesta:limitar el rango de caracteres a \x00-\x7F
rechazará casi todo dentro de este archivo.
Eso está muy mal y no eliminará ningún �
ya que no hay ninguno en ese archivo .
El uso de la prueba recomendada en esa respuesta eliminará 72.5 %
del archivo:
$ grep -oP "[^\x00-\x7F]" UTF-8-demo.txt | tr -d '\n' | wc -c
10192
$ cat UTF-8-demo.txt | wc -c
14058
Eso es (para la mayoría de los propósitos prácticos) el archivo completo. Un archivo muy bien diseñado para mostrar caracteres perfectamente válidos.
Prueba
El segundo archivo está diseñado para probar varios casos límite para confirmar que los lectores utf-8 están haciendo un buen trabajo. Contiene dentro muchos caracteres que harán que se muestre un '�'. Pero la otra recomendación de respuesta (la seleccionada) para usar file
falla groseramente con este archivo. Solo eliminando un byte cero (\0
) (que técnicamente es ASCII válido) y un \x7f
byte (DEL - eliminar) (que claramente también es un carácter ASCII) hará que todos el archivo válido para el 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
No solo file
fallan al detectar los muchos caracteres incorrectos, pero también fallan al detectar e informar que es un archivo codificado en UTF-8.
Y sí, file
es capaz de detectar y reportar texto codificado en UTF-8:
$ echo "ééakjfhhjhfakjfhfhaéá" | file -
/dev/stdin: UTF-8 Unicode text
Además, file
no informa como ASCII la mayoría de los caracteres de control en el rango de 1 a 31. (file
) reporta algunos rangos como data
:
$ printf '%b' "$(printf '\\U%x' {1..6})" | file -
/dev/stdin: data
Otros como ASCII text
:
$ printf '%b' "$(printf '\\U%x' 7 {9..12})" | file -
/dev/stdin: ASCII text
Como rango de caracteres imprimibles (con líneas nuevas):
$ printf '%b' "$(printf '\\U%x' {32..126} 10)" | file -
/dev/stdin: ASCII text
Pero algunos rangos pueden causar resultados extraños:
$ printf '%b' "$(printf '\\U%x' {14..26})" | file -
/dev/stdin: Atari MSA archive data, 4113 sectors per track, starting track: 5141, ending track: 5655
El programa file
no es una herramienta para detectar texto, sino para detectar magia números en programas o archivos ejecutables.
Los rangos file
detect, y el tipo correspondiente informado que encontré fue:
-
Valores de un byte, en su mayoría 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
-
Rangos codificados 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
Una posible solución se encuentra a continuación.
Respuesta anterior.
El valor Unicode para el carácter que está publicando es:
$ printf '%x\n' "'�"
fffd
Sí, ese es un carácter Unicode 'CARÁCTER DE REEMPLAZO' (U+FFFD). Ese es un carácter que se usa para reemplazar cualquier inválido Carácter Unicode encontrado en el texto. Es una "ayuda visual", no un personaje real. Para buscar y enumerar todas las líneas completas que contienen UNICODE no válidos uso de caracteres:
grep -axv '.*' out.txt
pero si solo desea detectar si algún carácter no es válido, use:
grep -qaxv '.*' out.txt; echo $?
Si el resultado es 1
el archivo está limpio, de lo contrario será cero 0
.
Si lo que preguntabas era:cómo encontrar el �
personaje, entonces, usa esto:
➤ a='Basically, if the file "out.txt" contains "�" anywhere in the file I'
➤ echo "$a" | grep -oP $(printf %b \\Ufffd)
�
O si su sistema procesa correctamente el texto UTF-8, simplemente:
➤ echo "$a" | grep -oP '�'
�
Esta respuesta muy temprana fue para la publicación original que era:
Cómo hacer grep para Unicode en un script bash
if grep -q "�" out.txt
then
echo "working"
else
cat out.txt fi
Básicamente, si el archivo "out.txt" contiene "�" en cualquier parte del archivo, me gustaría que repita "trabajando" Y si el archivo "out.txt" NO contiene "�" en ninguna parte del archivo, entonces me gustaría para gato fuera.txt
Prueba
grep -oP "[^\x00-\x7F]"
con un if .. then
declaración de la siguiente manera:
if grep -oP "[^\x00-\x7F]" file.txt; then
echo "grep found something ..."
else
echo "Nothing found!"
fi
Explicación:
-P
,--perl-regexp
:PATRÓN es una expresión regular de Perl-o
,--only-matching
:muestra solo la parte de una línea que coincide con el PATRÓN[^\x00-\x7F]
es una expresión regular para coincidir con un solo carácter no ASCII.[[:ascii:]]
- coincide con un único carácter ASCII[^[:ascii:]]
- coincide con un único carácter no ASCII
en bash
LC_COLLATE=C grep -o '[^ -~]' file