web-dev-qa-db-fra.com

Recherche de tous les fichiers "non binaires"

Est-il possible d'utiliser la commande find pour rechercher tous les fichiers "non binaires" dans un répertoire? Voici le problème que j'essaie de résoudre.

J'ai reçu une archive de fichiers d'un utilisateur Windows. Cette archive contient du code source et des fichiers image. Notre système de construction ne joue pas Nice avec des fichiers qui ont des fins de ligne Windows. J'ai un programme en ligne de commande (flip -u) qui inversera les fins de ligne entre * nix et windows. Donc, je voudrais faire quelque chose comme ça

find . -type f | xargs flip -u

Cependant, si cette commande est exécutée sur un fichier image ou un autre fichier multimédia binaire, elle corrompra le fichier. Je me rends compte que je pourrais construire une liste d'extensions de fichiers et filtrer avec cela, mais je préfère avoir quelque chose qui ne dépend pas de moi pour maintenir cette liste à jour.

Alors, existe-t-il un moyen de trouver tous les fichiers non binaires dans une arborescence de répertoires? Ou existe-t-il une autre solution que je devrais envisager?

46
Alan Storm

J'utiliserais file et dirigerais la sortie dans grep ou awk pour trouver des fichiers texte, puis extrairais juste la partie du nom de fichier de la sortie de file et la dirigerais dans xargs.

quelque chose comme:

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

Notez que le grep recherche le 'texte ASCII' plutôt que n'importe quel 'texte' - vous ne voulez probablement pas jouer avec les documents Rich Text ou les fichiers texte Unicode etc.

Vous pouvez également utiliser find (ou autre) pour générer une liste de fichiers à examiner avec file:

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

Le -d'\n' l'argument de xargs fait que xargs traite chaque ligne d'entrée comme un argument séparé, prenant ainsi en charge les noms de fichiers avec des espaces et d'autres caractères problématiques. c'est-à-dire que c'est une alternative à xargs -0 lorsque la source d'entrée ne génère pas ou ne peut pas générer de sortie séparée par NULL (comme find's -print0 option). Selon le changelog, xargs a obtenu le -d/--delimiter option en septembre 2005, donc devrait être dans n'importe quelle distribution Linux non ancienne (je ne savais pas, c'est pourquoi j'ai vérifié - je me suis juste souvenu vaguement que c'était un ajout "récent").

Notez qu'un saut de ligne est un caractère valide dans les noms de fichiers, donc cela se cassera si des noms de fichiers contiennent des sauts de ligne. Pour les utilisateurs Unix typiques, cela est pathologiquement insensé, mais n'est pas inconnu si les fichiers proviennent de machines Mac ou Windows.

Notez également que file n'est pas parfait. Il est très bon pour détecter le type de données dans un fichier, mais peut parfois être confondu.

J'ai utilisé de nombreuses variantes de cette méthode à plusieurs reprises dans le passé avec succès.

21
cas

Non. Un fichier binaire ou non binaire n'a rien de spécial. Vous pouvez utiliser des heuristiques comme "ne contient que des caractères dans 0x01–0x7F", mais cela appellera des fichiers texte avec des fichiers binaires de caractères non ASCII et des fichiers texte de fichiers binaires malchanceux.

Maintenant, une fois que vous l'avez ignoré ...

Fichiers Zip

S'il provient de votre utilisateur Windows sous forme de fichier Zip, le format Zip prend en charge le marquage des fichiers sous forme binaire ou texte dans l'archive elle-même. Vous pouvez utiliser la décompression de -a option pour faire attention à cela et convertir. Bien sûr, consultez le premier paragraphe pour savoir pourquoi cela peut ne pas être une bonne idée (le programme Zip a peut-être mal deviné lors de l'archivage).

zipinfo vous indiquera quels fichiers sont binaires (b) ou texte (t) dans sa liste de fichiers zip.

autres fichiers

La commande file examinera un fichier et essaiera de l'identifier. En particulier, vous trouverez probablement son -i (type de sortie MIME) option utile; convertir uniquement les fichiers de type texte/*

9
derobert

La réponse acceptée ne les a pas tous trouvés pour moi. Voici un exemple utilisant le grep's -I pour ignorer les binaires et ignorer tous les fichiers cachés ...

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

Ici, il est utilisé dans une application pratique: dos2unix

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

8
phyatt

Une solution générale pour traiter uniquement les fichiers non binaires dans bash en utilisant 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)

J'ai contacté l'auteur de l'utilitaire file et il a ajouté un astucieux -00 paramètre dans la version 5.26 (publiée le 2016-04-16, par exemple dans Arch actuel et Ubuntu 16.10) qui imprime file\0result\0 pour plusieurs fichiers alimentés en même temps, de cette façon, vous pouvez par exemple:

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

(La partie awk sert à filtrer tous les fichiers qui ne sont pas non binaires. ORS est le séparateur de sortie.)

Peut également être utilisé dans une boucle bien sûr:

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}')

Sur la base de cela et du précédent, j'ai créé un petit script bash pour filtrer les fichiers binaires qui utilise la nouvelle méthode en utilisant le -00 paramètre de file dans les versions plus récentes de celui-ci et revient à la méthode précédente sur les anciennes versions:

#!/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

Ou ici un plus POSIX-y, mais il nécessite le support de 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
7
phk
find . -type f -exec grep -I -q . {} \; -print

Cela trouvera tous les fichiers réguliers (-type f) dans le répertoire courant (ou ci-dessous) que grep pense être non vide et non binaire.

Il utilise grep -I pour distinguer les fichiers binaires des fichiers non binaires. Le -I flag et provoquera la fermeture de grep avec un état de sortie différent de zéro lorsqu'il détectera qu'un fichier est binaire. Un fichier "binaire" est, selon grep, un fichier qui contient des caractères en dehors de la plage imprimable ASCII.

Le -q option à grep provoquera sa fermeture avec un état de sortie nul si le modèle donné est trouvé, sans émettre de données. Le motif que nous utilisons est un seul point, qui correspondra à n'importe quel caractère.

Si le fichier s'avère non binaire et s'il contient au moins un caractère, le nom du fichier est imprimé.

Si vous vous sentez courageux, vous pouvez brancher votre flip -u aussi:

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

la réponse de Cas est bonne, mais elle suppose des noms de fichiers sensés ; en particulier, il est supposé que les noms de fichiers ne contiendront pas de nouvelles lignes.

Il n'y a aucune bonne raison de faire cette hypothèse ici, car il est assez simple (et en fait plus propre à mon avis) de gérer ce cas correctement également:

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

La commande find utilise uniquement fonctionnalités spécifiées par POSIX . En utilisant -exec pour exécuter des commandes arbitraires en tant que tests booléens est simple, robuste (gère correctement les noms de fichiers impairs) et plus portable que -print0.

En fait, toutes les parties de la commande sont spécifiées par POSIX à l'exception de flip.

Notez que file ne garantit pas l'exactitude des résultats qu'il renvoie. Cependant, dans la pratique, la recherche de "texte ASCII" dans sa sortie est assez fiable.

(Il peut manquer certains fichiers texte peut-être, mais il est très peu probable d'identifier incorrectement un fichier binaire en tant que "texte ASCII" et de le modifier — nous sommes donc pécher par excès de prudence.)

4
Wildcard

Essaye ça :

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

Où l'argument de grep '[^ -~]' est '[^<tab><space>-~]'.

Si vous le tapez sur une ligne de commande Shell, tapez Ctrl+V avant Tab. Dans un éditeur, il ne devrait y avoir aucun problème.

  • '[^<tab><space>-~]' correspondra à tout caractère qui n'est pas ASCII texte (les retours chariot sont ignorés par grep).
  • -L affichera uniquement le nom de fichier des fichiers qui ne correspondent pas
  • -Z affichera les noms de fichiers séparés par un caractère nul (pour xargs -0)
1
Vouze

Solution alternative:

La commande dos2unix convertira les fins de ligne de Windows CRLF en Unix LF et ignorera automatiquement les fichiers binaires. Je l'applique récursivement en utilisant:

find . -type f -exec dos2unix {} \;
1
Spark

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'): recherche les fichiers dans un chemin contenant le nom git et fichier avec le nom README. Si vous connaissez un dossier et un nom de fichier spécifiques à rechercher, il sera utile.

la commande ii.-exec exécute une commande sur le nom de fichier généré par find

iii. \; indique la fin de la commande

iv. {} est la sortie du fichier/nom de dossier trouvé lors de la recherche précédente

v. Plusieurs commandes peuvent être exécutées ultérieurement. En ajoutant -exec "command" \; comme avec -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. 

vous pouvez cloner ce répertoire de test et l'essayer: https://github.com/alphaCTzo7G/stackexchange/tree/master/linux/findSolution204092017

réponse plus détaillée ici: https://github.com/alphaCTzo7G/stackexchange/blob/master/linux/findSolution204092017/README.md

0
alpha_989