Encontrar todos los archivos "no binarios"

43

¿Es posible usar el findcomando para encontrar todos los archivos "no binarios" en un directorio? Aquí está el problema que estoy tratando de resolver.

Recibí un archivo de archivos de un usuario de Windows. Este archivo contiene código fuente y archivos de imagen. Nuestro sistema de compilación no funciona bien con archivos que tienen terminaciones de línea de Windows. Tengo un programa de línea de comandos ( flip -u) que cambiará las terminaciones de línea entre * nix y windows. Entonces, me gustaría hacer algo como esto

find . -type f | xargs flip -u

Sin embargo, si este comando se ejecuta contra un archivo de imagen u otro archivo multimedia binario, dañará el archivo. Me doy cuenta de que podría crear una lista de extensiones de archivo y filtrar con eso, pero prefiero tener algo que no dependa de que mantenga esa lista actualizada.

Entonces, ¿hay alguna manera de encontrar todos los archivos no binarios en un árbol de directorios? ¿O hay una solución alternativa que debería considerar?

Alan Storm
fuente
1
Puede usar la fileutilidad en algún lugar de su secuencia de comandos / canalización para identificar si el archivo es de datos o texto
lk-
1
¿Qué quiere decir con no binario (todo en una computadora moderna es binario). Supongo que está utilizando la distinción del antiguo sistema operativo C / PM, que tenía archivos de texto y binarios. Los archivos de texto pueden tener cualquier longitud pero deben terminar con un ctrl-z, y los archivos binarios deben ser múltiplos de un bloque de 512 bytes. Si es así, significa archivo de texto. (También noto que escribe sobre la línea que termina en archivos no binarios, esto también sugeriría que son archivos de texto) ¿Es esto correcto?
ctrl-alt-delor
Todos los archivos son binarios, es solo una cuestión de interpretación. ¿Estás preguntando cómo encontrar archivos de texto?
ctrl-alt-delor
@richard Vengo a formar una era en la que llamamos archivos destinados a ser interpretados como texto sin formato y todos los demás archivos (imágenes, documentos de procesamiento de texto, etc.) binarios. Sé que todo es solo uno y ceros debajo del capó :)
Alan Storm
1
Ah, entiendo lo que quieres decir con mis términos: usaré binario / texto en el futuro para evitar confusiones. Re: la cosa \ r \ n - entiendo que esos son los caracteres ASCII para el retorno de carro de una máquina de escribir (mover al principio de la línea) y el avance de línea (mover una línea hacia abajo). Entonces, \ r \ n es un modelo "más preciso" de la cosa física del mundo real para la que era un personaje de fin de línea. Pre OS X, Macs usaba solo un \ r para esto. Por lo general, escribo todo como "elecciones arbitrarias hechas con prisa con las que todavía estamos lidiando"
Alan Storm

Respuestas:

20

Usaría filey canalizaría la salida en grep o awk para encontrar archivos de texto, luego extraería solo la parte del nombre del archivo de filela salida y la canalizaría en xargs.

algo como:

file * | awk -F: '/ASCII text/ {print $1}' | xargs -d'\n' -r flip -u

Tenga en cuenta que el grep busca 'texto ASCII' en lugar de cualquier 'texto'; probablemente no quiera meterse con documentos de texto enriquecido o archivos de texto unicode, etc.

También puede usar find(o lo que sea) para generar una lista de archivos para examinar con file:

find /path/to/files -type f -exec file {} + | \
  awk -F: '/ASCII text/ {print $1}' | xargs -d'\n' -r flip -u

El -d'\n'argumento de xargs hace que xargs trate cada línea de entrada como un argumento separado, atendiendo así a los nombres de archivos con espacios y otros caracteres problemáticos. es decir, es una alternativa a xargs -0cuando la fuente de entrada no genera o no puede generar una salida separada por NULL (como findla -print0opción de '). De acuerdo con el registro de cambios, xargs obtuvo la opción -d/ --delimiteren septiembre de 2005, por lo que debería estar en cualquier distribución de Linux no antigua (no estaba seguro, por eso lo verifiqué, solo recordaba vagamente que era una adición "reciente").

Tenga en cuenta que un salto de línea es un carácter válido en los nombres de archivo, por lo que se interrumpirá si algún nombre de archivo tiene un salto de línea. Para los usuarios típicos de Unix, esto es patológicamente loco, pero no se desconoce si los archivos se originaron en máquinas Mac o Windows.

También tenga en cuenta que fileno es perfecto. Es muy bueno para detectar el tipo de datos en un archivo, pero en ocasiones puede confundirse.

He usado numerosas variaciones de este método muchas veces en el pasado con éxito.

cas
fuente
1
¡Gracias por esta solución! Por alguna razón, se filemuestra en English textlugar de ASCII texten mi sistema Solaris, por lo que modifiqué esa parte en consecuencia. Además, lo reemplacé awk -F: '{print $1}'con el equivalente cut -f1 -d:.
Andrew Cheong
3
vale la pena decir grep -Ifiltros binarios
xenoterracide
Buscar la palabra textdebería ser suficiente. Esto también recogerá filedescripciones como ASCII Java program texto HTML document texto troff or preprocessor input text.
user1024
Mi respuesta es parcialmente una respuesta / mejora sobre esta respuesta. Muy buen punto sobre grepping para ASCII textevitar estropear los RTF.
Comodín el
1
xenoterracide: Me salvaste la vida, hombre! Solo una bandera -Y BINGO
Sergio Abreu
9

No. No hay nada especial en un archivo binario o no binario. Puede usar heurísticas como 'contiene solo caracteres en 0x01–0x7F', pero eso llamará archivos de texto con archivos binarios de caracteres no ASCII y archivos de texto de archivos binarios desafortunados.

Ahora, una vez que has ignorado eso ...

archivos zip

Si proviene de su usuario de Windows como un archivo zip, el formato zip admite marcar archivos como binarios o texto en el propio archivo. Puede usar la -aopción de descomprimir para prestar atención a esto y convertir. Por supuesto, vea el primer párrafo sobre por qué esto puede no ser una buena idea (el programa zip puede haber adivinado mal cuando creó el archivo).

zipinfo le dirá qué archivos son binarios (b) o de texto (t) en su lista de archivos zip.

otros archivos

El comando de archivo mirará un archivo e intentará identificarlo. En particular, probablemente encontrará -iútil su opción (tipo MIME de salida); solo convierte archivos con texto de tipo / *

derobert
fuente
6

Una solución general para procesar solo archivos no binarios al bashusar file -b --mime-encoding:

while IFS= read -d '' -r file; do
  [[ "$(file -b --mime-encoding "$file")" = binary ]] &&
    { echo "Skipping   $file."; continue; }

  echo "Processing $file."

  # ...

done < <(find . -type f -print0)

Me puse en contacto con el autor de la utilidad de archivo y agregó un ingenioso -00parámetro en la versión 5.26 (lanzada el 16/04/2016, por ejemplo, en Arch y Ubuntu 16.10 actual) que imprime file\0result\0para múltiples archivos alimentados a la vez, de esta manera puede hacerlo p.ej:

find . -type f -exec file -00 --mime-encoding {} + |
  awk 'BEGIN{ORS=RS="\0"}{if(NR%2)f=$0;else if(!/binary/)print f}' | 

(La awkparte es filtrar cada archivo que no es no binario. ORSEs el separador de salida).

También se puede usar en un bucle, por supuesto:

while IFS= read -d '' -r file; do

  echo "Processing $file."

  # ...

done < <(find . -type f -exec file -00 --mime-encoding {} + |
  awk 'BEGIN{ORS=RS="\0"}{if(NR%2)f=$0;else if(!/binary/)print f}')

Basado en esto y en lo anterior, creé un pequeño bashscript para filtrar archivos binarios que utiliza el nuevo método usando el -00parámetro de fileen versiones más nuevas y vuelve al método anterior en versiones anteriores:

#!/bin/bash

# Expects files as arguments and returns the ones that do
# not appear to be binary files as a zero-separated list.
#
# USAGE:
#   filter_binary_files.sh [FILES...]
#
# EXAMPLE:
#   find . -type f -mtime +5 -exec ./filter_binary_files.sh {} + | xargs -0 ...
# 

[[ $# -eq 0 ]] && exit

if [[ "$(file -v)" =~ file-([1-9][0-9]|[6-9]|5\.([3-9][0-9]|2[6-9])) ]]; then
  file -00 --mime-encoding -- "$@" |
    awk 'BEGIN{ORS=RS="\0"}{if(NR%2)f=$0;else if(!/binary/)print f}'
else
  for f do
    [[ "$(file -b --mime-encoding -- "$f")" != binary ]] &&
      printf '%s\0' "$f"
  done
fi

O aquí uno más POSIX-y, pero requiere soporte para sort -V:

#!/bin/sh

# Expects files as arguments and returns the ones that do
# not appear to be binary files as a zero-separated list.
#
# USAGE:
#   filter_binary_files.sh [FILES...]
#
# EXAMPLE:
#   find . -type f -mtime +5 -exec ./filter_binary_files.sh {} + | xargs -0 ...
# 

[ $# -eq 0 ] && exit

if [ "$(printf '%s\n' 'file-5.26' "$(file -v | head -1)" | sort -V)" = \
    'file-5.26' ]; then
  file -00 --mime-encoding -- "$@" |
    awk 'BEGIN{ORS=RS="\0"}{if(NR%2)f=$0;else if(!/binary/)print f}'
else
  for f do
    [ "$(file -b --mime-encoding -- "$f")" != binary ] &&
      printf '%s\0' "$f"
  done
fi
phk
fuente
6

La respuesta aceptada no los encontró todos para mí. Aquí hay un ejemplo usando grep's -Ipara ignorar binarios e ignorando todos los archivos ocultos ...

find . -type f -not -path '*/\.*' -exec grep -Il '.' {} \; | xargs -L 1 echo 

Aquí está en uso en una aplicación práctica: dos2unix

https://unix.stackexchange.com/a/365679/112190

phyatt
fuente
4

La respuesta de Cas es buena, pero supone nombres de archivo sanos ; en particular se supone que los nombres de archivo no contendrán nuevas líneas.

No hay una buena razón para hacer esta suposición aquí, ya que es bastante simple (y en realidad más limpio en mi opinión) manejar ese caso correctamente también:

find . -type f -exec sh -c 'file "$1" | grep -q "ASCII text"' sh {} \; -exec flip -u {} \;

El findcomando solo utiliza funciones especificadas por POSIX . El uso -execpara ejecutar comandos arbitrarios como pruebas booleanas es simple, robusto (maneja nombres de archivo impares correctamente) y más portátil que -print0.

De hecho, POSIX especifica todas las partes del comando, excepto flip.

Tenga en cuenta que fileno garantiza la precisión de los resultados que devuelve. Sin embargo, en la práctica, el grepping para "texto ASCII" en su salida es bastante confiable.

(Es posible que falten algunos archivos de texto, pero es muy poco probable que identifique incorrectamente un archivo binario como "texto ASCII" y lo destruya, por lo que estamos equivocados).

Comodín
fuente
El archivo sin argumentos callspuede ser bastante lento, por ejemplo, para videos le dirá todo sobre la codificación.
phk
También está asumiendo que ningún archivo comienza con -.
phk
Y no veo ninguna razón por la que no haría una sola llamada file, puede tomar varios archivos como argumentos.
phk
@phk, para abordar sus comentarios: (1) es bueno saber la posible lentitud, pero no veo una forma POSIX para evitar eso; (2) Hago cero suposiciones sobre los nombres de archivo, ya que el findcomando prefijará ./a cualquier nombre de archivo pasado al comando de shell; (3) Usar grepcomo prueba en una filesalida de comando único a la vez es la única forma POSIX que puedo ver para garantizar el manejo correcto de los nombres de archivo que pueden contener nuevas líneas.
Comodín el
Revisé su solución final "POSIX-y" y creo que es inteligente, pero asume que es filecompatible con la --mime-encodingbandera y el --separador, ninguno de los cuales está garantizado por POSIX .
Comodín el
2
find . -type f -exec grep -I -q . {} \; -print

Esto encontrará todos los archivos regulares ( -type f) en el directorio actual (o inferior) que greppiensa que no están vacíos ni son binarios.

Se utiliza grep -Ipara distinguir entre archivos binarios y no binarios. La -Imarca y hará grepque salga con un estado de salida distinto de cero cuando detecte que un archivo es binario. Un archivo "binario" es, de acuerdo con grep, un archivo que contiene caracteres fuera del rango ASCII imprimible.

La -qopción de grephará que se cierre con un estado de salida cero si se encuentra el patrón dado, sin emitir ningún dato. El patrón que usamos es un solo punto, que coincidirá con cualquier carácter.

Si se encuentra que el archivo no es binario y contiene al menos un carácter, se imprime el nombre del archivo.

Si te sientes valiente, también puedes enchufarlo flip -u:

find . -type f -exec grep -I -q . {} \; -print -exec flip -u {} \;
Kusalananda
fuente
1

Prueba esto :

find . -type f -print0 | xargs -0 -r grep -Z -L -U '[^         -~]' | xargs -0 -r flip -u

Donde el argumento de grep '[^ -~]'es '[^<tab><space>-~]'.

Si lo escribe en una línea de comando de shell, escriba Ctrl+ Vantes Tab. En un editor, no debería haber ningún problema.

  • '[^<tab><space>-~]'coincidirá con cualquier carácter que no sea texto ASCII (los retornos de carro se ignoran grep).
  • -L imprimirá solo el nombre de archivo de los archivos que no coinciden
  • -Zgenerará nombres de archivo separados con un carácter nulo (para xargs -0)
Vouze
fuente
Vale la pena señalar que con Perl-like Regex grep -P(si está disponible) \testá disponible. Alternativamente, usando la traducción local si el shell lo admite: $'\t'( bashy zshhacer).
phk
1

Solución alternativa:

El comando dos2unix convertirá los finales de línea de Windows CRLF a Unix LF y omitirá automáticamente los archivos binarios. Lo aplico recursivamente usando:

find . -type f -exec dos2unix {} \;
Chispa
fuente
Dado que dos2unixpuede tomar varios nombres de archivo como argumento, es mucho más eficiente hacerlofind . -type f -exec dos2unix {} +
Anthon
0

sudo find / (-type f -and -path '* / git / *' -iname 'README') -exec grep -liI '100644 \ | 100755' {} \; -exec flip -u {} \;

i. (-type f -and -path '* / git / *' -iname 'README'): busca archivos dentro de una ruta que contenga el nombre git y el archivo con el nombre README. Si conoce alguna carpeta y nombre de archivo específicos para buscar, será útil.

El comando ii.-exec ejecuta un comando en el nombre del archivo generado por find

iii. \; indica el fin del comando

iv. {} es el resultado del archivo / nombre de carpeta encontrado en la búsqueda de búsqueda anterior

v. Se pueden ejecutar varios comandos posteriormente. Al agregar -exec "comando" \; como con -exec flip -u \;

vii.grep

1.-l lists the name of the file
2.-I searches only non-binary files
3.-q quiet output
4.'100644\|100755' searches for either 100644 or 100755 within the file found. if found it then runs flip -u. \| is the or operator for grep. 

puede clonar este directorio de prueba y probarlo: https://github.com/alphaCTzo7G/stackexchange/tree/master/linux/findSolution204092017

respuesta más detallada aquí: https://github.com/alphaCTzo7G/stackexchange/blob/master/linux/findSolution204092017/README.md

alpha_989
fuente