shell - leer - modificar archivo binario en c
¿Cómo comprobar si el archivo es un archivo binario y leer todos los archivos que no lo son? (12)
¿Cómo puedo saber si un archivo es un archivo binario?
Por ejemplo, archivo c compilado.
Quiero leer todos los archivos de algún directorio, pero quiero ignorar los archivos binarios.
BSD grep
Aquí hay una solución simple para buscar un solo archivo usando BSD grep
(en macOS / Unix):
grep -q "/x00" file && echo Binary || echo Text
que básicamente verifica si el archivo contiene el carácter NUL.
Usando este método, para leer todos los archivos no binarios de forma recursiva usando la utilidad de find
, puede hacer:
find . -type f -exec sh -c ''grep -q "/x00" {} || cat {}'' ";"
O incluso más simple usando solo grep
:
grep -rv "/x00" .
Para la carpeta actual, use:
grep -v "/x00" *
Lamentablemente, los ejemplos anteriores no funcionarán para GNU grep
, sin embargo, hay una solución.
GNU grep
Como GNU grep
está ignorando caracteres NULL, es posible verificar otros caracteres que no sean ASCII, como:
$ grep -P "[^/x00-/x7F]" file && echo Binary || echo Text
Nota: No funcionará para archivos que solo contengan caracteres NULL.
cat
+ grep
Suponiendo que binario significa el archivo que contiene caracteres NULL, este comando de shell puede ayudar:
(cat -v file.bin | grep -q "/^@") && echo Binary || echo Text
o:
grep -q "/^@" <(cat -v file.bin) && echo Binary
Esta es la solución para grep -q "/x00"
, que funciona para BSD grep, pero no para la versión de GNU.
Básicamente -v
para cat
convierte todos los caracteres no imprimibles para que sean visibles en forma de caracteres de control, por ejemplo:
$ printf "/x00/x00" | hexdump -C
00000000 00 00 |..|
$ printf "/x00/x00" | cat -v
^@^@
$ printf "/x00/x00" | cat -v | hexdump -C
00000000 5e 40 5e 40 |^@^@|
donde ^@
caracteres representan el carácter NULL. Entonces, una vez que se encuentran estos caracteres de control, suponemos que el archivo es binario.
La desventaja del método anterior es que podría generar falsos positivos cuando los caracteres no representan caracteres de control. Por ejemplo:
$ printf "/x00/x00^@^@" | cat -v | hexdump -C
00000000 5e 40 5e 40 5e 40 5e 40 |^@^@^@^@|
Ver también: ¿Cómo grep para todos los caracteres que no sean ASCII ?
grep
Suponiendo que el archivo binario significa que contiene caracteres no imprimibles (excluyendo caracteres en blanco como espacios, pestañas o nuevos caracteres de línea), esto puede funcionar (tanto BSD como GNU):
$ grep ''[^[:print:][:blank:]]'' file && echo Binary || echo Text
Nota: GNU grep
informará el archivo que contiene solo caracteres NULL como texto, pero funcionaría correctamente en la versión BSD .
Para ver más ejemplos, ver: ¿Cómo grep para todos los caracteres que no sean ASCII ?
Adaptado de excluir archivo binario
find . -exec file {} /; | grep text | cut -d: -f1
Es una especie de fuerza bruta excluir archivos binarios con tr -d "[[:print:]/n/t]" < file | wc -c
tr -d "[[:print:]/n/t]" < file | wc -c
, pero tampoco es una conjetura heurística.
find . -type f -maxdepth 1 -exec /bin/sh -c ''
for file in "$@"; do
if [ $(LC_ALL=C LANG=C tr -d "[[:print:]/n/t]" < "$file" | wc -c) -gt 0 ]; then
echo "${file} is no ASCII text file (UNIX)"
else
echo "${file} is ASCII text file (UNIX)"
fi
done
'' _ ''{}'' +
Sin embargo, el siguiente enfoque de fuerza bruta usando grep -a -m 1 $''[^[:print:]/t]'' file
parece bastante más rápido.
find . -type f -maxdepth 1 -exec /bin/sh -c ''
tab="$(printf "/t")"
for file in "$@"; do
if LC_ALL=C LANG=C grep -a -m 1 "[^[:print:]${tab}]" "$file" 1>/dev/null 2>&1; then
echo "${file} is no ASCII text file (UNIX)"
else
echo "${file} is ASCII text file (UNIX)"
fi
done
'' _ ''{}'' +
Pruebe la siguiente línea de comando:
file "$FILE" | grep -vq ''ASCII'' && echo "$FILE is binary"
Saliendo de la sugerencia de Bach , creo que --mime-encoding
es la mejor --mime-encoding
para obtener algo confiable del file
.
file --mime-encoding [FILES ...] | grep -v ''/bbinary$''
imprimirá los archivos que el file
cree que tienen una codificación no binaria. Puede canalizar esta salida a través de cut -d: -f1
para recortar la : encoding
si solo desea los nombres de archivo.
Advertencia: como @yugr informa a continuación, los archivos .doc
informan una codificación de la application/mswordbinary
. Esto me parece un error: el tipo MIME se está concatenando erróneamente con la codificación.
$ for flag in --mime --mime-type --mime-encoding; do
echo "$flag"
file "$flag" /tmp/example.{doc{,x},png,txt}
done
--mime
/tmp/example.doc: application/msword; charset=binary
/tmp/example.docx: application/vnd.openxmlformats-officedocument.wordprocessingml.document; charset=binary
/tmp/example.png: image/png; charset=binary
/tmp/example.txt: text/plain; charset=us-ascii
--mime-type
/tmp/example.doc: application/msword
/tmp/example.docx: application/vnd.openxmlformats-officedocument.wordprocessingml.document
/tmp/example.png: image/png
/tmp/example.txt: text/plain
--mime-encoding
/tmp/example.doc: application/mswordbinary
/tmp/example.docx: binary
/tmp/example.png: binary
/tmp/example.txt: us-ascii
Usted puede hacer esto también aprovechando el comando diff
. Verifique esta respuesta:
Utilice el operador de prueba de archivo -T
incorporado de Perl, preferiblemente después de comprobar que se trata de un archivo simple utilizando el operador de prueba de archivo -f
:
$ perl -le ''for (@ARGV) { print if -f && -T }'' /
getwinsz.c a.out /etc/termcap /bin /bin/cat /
/dev/tty /usr/share/zoneinfo/UTC /etc/motd
getwinsz.c
/etc/termcap
/etc/motd
Aquí está el complemento de ese conjunto:
$ perl -le ''for (@ARGV) { print unless -f && -T }'' /
getwinsz.c a.out /etc/termcap /bin /bin/cat /
/dev/tty /usr/share/zoneinfo/UTC /etc/motd
a.out
/bin
/bin/cat
/dev/tty
/usr/share/zoneinfo/UTC
yo suelo
! grep -qI . $path
La única desventaja que puedo ver es que considerará un archivo binario vacío pero, una vez más, ¿quién decide si está mal?
Usar file
utilidad, uso de muestra:
$ file /bin/bash
/bin/bash: Mach-O universal binary with 2 architectures
/bin/bash (for architecture x86_64): Mach-O 64-bit executable x86_64
/bin/bash (for architecture i386): Mach-O executable i386
$ file /etc/passwd
/etc/passwd: ASCII English text
$ file code.c
code.c: ASCII c program text
perl -E ''exit((-B $ARGV[0])?0:1);'' file-to-test
Podría usarse para verificar siempre que "archivo-a-prueba" sea binario. El comando anterior saldrá con el código 0 en archivos binarios, de lo contrario, el código de salida sería 1.
La comprobación inversa del archivo de texto puede parecerse al siguiente comando:
perl -E ''exit((-T $ARGV[0])?0:1);'' file-to-test
Del mismo modo, el comando anterior saldrá con el estado 0 si el "archivo a prueba" es texto (no binario).
Lea más sobre las comprobaciones -B
y -T
usando el comando perldoc -f -X
.