¿Hay alguna manera conveniente de clasificar los archivos como "binarios" o "texto"?


35

A las utilidades estándar de Unix les gusta grepy diffusan algunas heurísticas para clasificar archivos como "texto" o "binario". (Por ejemplo grep, la salida puede incluir líneas como Binary file frobozz matches.)

¿Existe una prueba conveniente que se pueda aplicar en un zshscript para realizar una clasificación similar de "texto / binario"? (Aparte de algo como grep '' somefile | grep -q Binary).

(Me doy cuenta de que cualquier prueba de este tipo sería necesariamente heurística y, por lo tanto, imperfecta).


10
filees una utilidad estándar y puede ejecutar la magia de archivos para determinar los tipos de archivos lo mejor que pueda. Puede indicar la mayoría de los formatos de texto y hace un trabajo bastante decente en formatos binarios. Si todo lo que intenta hacer es averiguar si un archivo es texto o no, ese es el comando que le interesa.
Bratchley

@Bratchley: filese imprimirán algunas versiones de , por ejemplo shell script, para algunos archivos que me gustaría clasificar como "texto". ¿Hay alguna manera filede imprimir solo texto binary?
kjo

1
@don_crissti Esa pregunta es sobre alguien tratando de hacer que la gente depure su script de bash. Detectar texto es justo lo que se supone que debe hacer el script. Terminaron teniendo un problema en uno de sus cutcomandos.
Bratchley

1
@don_crissti El hecho de que haya una respuesta en la pregunta A que funcione para la pregunta B no siempre convierte a A en un duplicado de B. Considere a alguien que está buscando una manera de clasificar archivos como texto o binario. ¿Qué es más útil: una pregunta de "depuración de mi script" que tiene una respuesta genérica enterrada entre otras respuestas que son específicas de ese script, o un genérico "¿cómo clasifico los campos como texto o binario?"
Gilles 'SO- deja de ser malvado'

1
@Gilles: depende de cómo lo leas. De hecho, veo la pregunta allí como un caso típico de problema XY: OP allí quiere verificar si un archivo es un archivo de texto, y cree que la filesalida de tubería cutes la solución, claro, falta un espacio que lo hace fallar y eso ha hecho la mayoría de las personas allí abordan la Y en lugar de la X, pero los comentarios y respuestas de Stéphane muestran la forma correcta de determinar si el archivo es texto o no.
don_crissti

Respuestas:


27

Si solicita filesolo el tipo mime , obtendrá muchos diferentes, como text/x-shellscript, application/x-executableetc., pero me imagino que si solo verifica la parte de "texto", obtendrá buenos resultados. Por ejemplo ( -bpara ningún nombre de archivo en la salida):

file -b --mime-type filename | sed 's|/.*||'

24
Sólo recuerde, dependiendo de su file, que es posible que pierda algunos formatos de texto: application/xml(y similar como RSS), application/ecmascript, application/json, image/svg+xml, ... Habría que la lista blanca aquellos.
Boldewyn

@Boldewyn wow, buenos ejemplos! Entonces, probablemente una mejor respuesta es aceptar cualquier archivo que solo tenga caracteres imprimibles, pero que de alguna manera también haga frente a utf-8 y problemas de codificación similares.
meuh

Sí, esa es la esencia de mi respuesta a continuación. El único problema es que esa solución tiene que mirar todo el archivo ...
Boldewyn

77
@Boldewyn En principio, los application/*tipos no están destinados al consumo humano, incluso cuando pueden estar basados ​​en texto para facilitar el desarrollo y la depuración. Es por eso que hay tanto a text/xmlcomo a application/xml. Entonces, la pregunta de si considerarlos como texto depende de las necesidades del OP.
Tobia

3
Ocut -d/ -f1
Stéphane Chazelas

20

Otro enfoque sería usar isutf8de la colección moreutils .

Sale con 0 si el archivo es válido UTF-8 o ASCII, o cortocircuitos, imprime un mensaje de error (silencio con -q) y sale con 1 de lo contrario.


55
Buena sugerencia Acabo de notar que dar un directorio como arg hace que devuelva 0. Hubiera preferido 1 al menos. Pero entonces, basura adentro, basura afuera.
meuh

13

Si te gusta la heurística utilizada por GNU grep, puedes usarla:

isbinary() {
  LC_MESSAGES=C grep -Hm1 '^' < "${1-$REPLY}" | grep -q '^Binary'
}

Se busca NUL bytes en el primer tampón lee desde el archivo (unos kilo-bytes de un archivo normal, pero podría ser mucho menos por un tubo o de tubo o algunos dispositivos como /dev/random). En las configuraciones regionales UTF-8, también marca las secuencias de bytes que no forman caracteres UTF-8 válidos. Asume LC_ALLque no está configurado para algo donde el idioma no sea inglés.

El ${1-$REPLY}formulario le permite usarlo como zshcalificador global:

ls -ld -- *(.+isbinary)

enumeraría los archivos binarios .


7

Puede intentar determinar si iconvpuede leer el archivo. Esto tiene menos rendimiento que file(que solo lee un par de bytes desde el principio), pero le dará resultados más confiables:

ENCODING=utf-8
if iconv --from-code="$ENCODING" --to-code="$ENCODING" your_file.ext > /dev/null 2>&1; then
    echo text
else
    echo binary
fi

Esto iconvbásicamente hace que no funcione, pero si encuentra datos no válidos (UTF-8 no válido en este ejemplo), vomitará y saldrá.


44
Usar -fy en -tlugar de las opciones largas de GNU lo haría más portátil. Tenga en cuenta que llamará "binario" a los archivos que no puede abrir. Llamará a los archivos vacíos "texto".
Stéphane Chazelas

Convenido. Usé los formularios largos para documentación ad hoc, para personas que no saben iconv. Pero -fy -tgeneralmente son mejores.
Boldewyn

7

Puede escribir un script que llame filey usar una declaración de caso para verificar los casos que le interesan.

Por ejemplo

#!/bin/sh
case $(file "$1") in
(*script*|*\ text|*\ text\ *)
    echo text
    ;;
(*)
    echo binary
    ;;
esac

aunque, por supuesto, puede haber muchos casos especiales que sean de interés. Solo comprobando stringsuna copia de libmagic, veo unos 200 casos, por ejemplo,

Konqueror cookie text
Korn shell script text executable
LaTeX 2e document text
LaTeX document text
Linux Software Map entry text
Linux Software Map entry text (new format)
Linux kernel symbol map text
Lisp/Scheme program text
Lua script text executable
LyX document text
M3U playlist text
M4 macro processor script text

Algunos usan la cadena "texto" como parte de un tipo diferente, por ejemplo,

SoftQuad troff Context intermediate   
SoftQuad troff Context intermediate for AT&T 495 laser printer
SoftQuad troff Context intermediate for HP LaserJet

igualmente scriptpodría ser parte de una palabra, pero no veo problemas en este caso. Pero una secuencia de comandos debe comprobar "text"como una palabra , no una subcadena .

Como recordatorio, la filesalida no utiliza una descripción precisa que siempre tenga "script" o "text". Los casos especiales son algo a considerar. Un seguimiento comentó que --mime-typefunciona mientras que este enfoque no lo haría, para los .svgarchivos. Sin embargo, en una prueba veo estos resultados para archivos svg:

$ ls -l *.svg
-r--r--r-- 1 tom users  6679 Jul 26  2012 pumpkin_48x48.svg
-r--r--r-- 1 tom users 17372 Jul 30  2012 sink_48x48.svg
-r--r--r-- 1 tom users  5929 Jul 25  2012 vile_48x48.svg
-r--r--r-- 1 tom users  3553 Jul 28  2012 vile-mini.svg
$ file *.svg
pumpkin_48x48.svg: SVG Scalable Vector Graphics image
sink_48x48.svg:    SVG Scalable Vector Graphics image
vile-mini.svg:     SVG Scalable Vector Graphics image
vile_48x48.svg:    SVG Scalable Vector Graphics image
$ file --mime-type *.svg
pumpkin_48x48.svg: image/svg+xml
sink_48x48.svg:    image/svg+xml
vile-mini.svg:     image/svg+xml
vile_48x48.svg:    image/svg+xml

que seleccioné después de ver que mil archivos muestran solo 6 con "texto" en la salida de tipo mime. Podría decirse que hacer coincidir el "xml" al final de la salida de tipo mime podría ser más útil, por ejemplo, que hacer coincidir "SVG", pero usar un script para hacerlo lo lleva de vuelta a la sugerencia que se hace aquí.

El resultado de filerequiere un poco de ajuste en cualquier escenario, y no es 100% confiable (varios de mis scripts de Perl lo confunden, llamándolos "datos").

Hay más de una implementación de file. El que se usa más comúnmente hace su trabajo libmagic, que puede usarse desde diferentes programas (quizás no directamente desde zsh, aunque pythonpuede).

De acuerdo con la tabla de comparación de pruebas de archivos para shell, Perl, Ruby y Python , Perl tiene una -Topción que puede usar para proporcionar esta información. Pero no enumera ninguna característica comparable para zsh.

Otras lecturas:


Desafortunadamente file, la salida de GNU para archivos svg: SVG Scalable Vector Graphics imageno contiene la palabra texto. Pensé que este enfoque sería mejor que la respuesta aceptada de verificar el tipo MIME, pero aún falta algunos tipos.
Peter Cordes

Todavía se pierde, con el tipo mimo; para el archivo svg de xterm que obtengo image/svg+xml. En realidad, solo verifiqué un archivo de 1000, solo 6 salieron como "texto" de acuerdo con el tipo mime solo. Seguiré con un script, que al menos se puede hacer que funcione según sea necesario.
Thomas Dickey

3

filetiene una opción --mime-encodingque intenta detectar la codificación de un archivo.

 $file --mime-encoding Documents/poster2.pdf 
Documents/poster2.pdf: binary
 $file --mime-encoding projects/linux/history-torvalds/Makefile 
projects/linux/history-torvalds/Makefile: us-ascii
 $file --mime-encoding graphe.tex 
Dgraphe.tex: us-ascii
 $file --mime-encoding software.tex 
software.tex: utf-8

Puede usar file --mime-encoding | grep binarypara detectar si un archivo es un archivo binario. Funciona de manera confiable, aunque puede confundirse con un solo carácter no válido en un archivo de texto largo.

Por ejemplo, alias catal siguiente script de shell para evitar arruinar mi terminal al abrir inadvertidamente un archivo binario:

#! /bin/sh -

[ ! -t 1 ] && exec /bin/cat "$@"
for i
do
    if file --mime-encoding -- "$i" | grep -q binary
    then
        hexdump -C -- "$i"
    else
        /bin/cat -- "$i"
    fi
done

3

Las categorías son arbitrarias. Antes de responder cómo hacer una clasificación, necesita una definición (estricta). Para tener una definición, necesitas un propósito .

Entonces, ¿qué quieres hacer con esa clasificación?

  • Si desea seleccionar ascii / binary en FTP, es importante que no transfiera un archivo binario como ascii (o se dañará). Por lo tanto, debe probar si el archivo es texto plano, html, rtf y algunos otros. Pero en caso de duda, seleccione binario. Y tal vez también desee probar que el archivo solo tiene un subconjunto como 0x0A, 0x0D y 0x20-0x7F.
  • Si desea transferir el archivo en algún protocolo (POP3, SMTP), debe probar para elegir si codifica en base64 o simplemente. En este caso, debe probar si hay caracteres no compatibles.
  • Cualquier otro caso ... puede tener cualquier otra definición.

3
perl -e'chomp(my$f=<>);print "binary$/" if -B $f;print "text$/" if -T _'

lo haré. Consulte la documentación para -By-T (busque en esa página la cadena The -T and -B switches work as follows).


perl -le 'print -B $ARGV[0] ? "binary" : "text"' --podría ser más claro O inclusoperl -le 'print -B $_ ? "binary" : "text", @ARGV > 1 ? "\t$_" : "" for @ARGV' --
jrw32982 es compatible con Monica el

1

Contribuí a https://github.com/audreyr/binaryornot. Todavía no tiene un contenedor de línea de comandos, pero esta es una biblioteca simple de Python lo suficientemente fácil de llamar incluso desde la CLI. Utiliza una heurística bastante eficiente para determinar si un archivo es de texto o binario.


1

Ahora esta respuesta es un poco vieja, pero creo que mi amigo me enseñó un gran "truco" para hacer esto.

Utiliza el diffcomando y comprueba tu archivo con un archivo de texto de prueba:

$ diff filetocheck testfile.txt

Ahora si filetocheckes un archivo binario, la salida sería:

Binary files filetocheck and testfile.txt differ

De esta manera, podría aprovechar el diffcomando y, por ejemplo, escribir una función que verifique en un script.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.