Recherche de tous les fichiers «non binaires»

43

Est-il possible d'utiliser la findcommande 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 le code source et les fichiers image. Notre système de construction ne fonctionne pas bien avec les fichiers avec des fins de lignes Windows. J'ai un programme en ligne de commande ( flip -u) qui basculera les fins de ligne entre * nix et windows. Donc, j'aimerais faire quelque chose comme ça

find . -type f | xargs flip -u

Toutefois, si cette commande est exécutée sur un fichier image ou un autre fichier multimédia binaire, le fichier sera corrompu. Je me rends compte que je pourrais construire une liste d'extensions de fichiers et filtrer avec cela, mais je préférerais que quelque chose ne dépende pas de moi pour maintenir cette liste à jour.

Alors, est-il possible de trouver tous les fichiers non binaires dans une arborescence de répertoires? Ou y a-t-il une solution alternative que je devrais envisager?

Alan Storm
la source
1
Vous pouvez utiliser l' fileutilitaire quelque part dans votre script / pipeline pour déterminer s'il s'agit d'un fichier de données ou de texte
lk-
1
Qu'entendez-vous par non-binaire (sur un ordinateur moderne, tout est binaire). Je suppose que vous utilisez la distinction de l'ancien système d'exploitation C / PM, qui contenait du texte et des fichiers binaires. Les fichiers texte peuvent être de n'importe quelle longueur mais doivent se terminer par un ctrl-z et les fichiers binaires doivent être un multiple d'un bloc de 512 octets. Si c'est le cas, vous voulez dire fichier texte. (Je remarque également que vous écrivez à propos des lignes se terminant par des fichiers non binaires, cela suggère également qu'il s'agit de fichiers texte) Est-ce correct?
ctrl-alt-delor
Tous les fichiers sont binaires, c’est juste une question d’interprétation. Vous demandez comment trouver des fichiers texte?
ctrl-alt-delor
@richard Je viens d'une époque où nous appelions des fichiers censés être interprétés comme du texte brut en texte brut , et tous les autres fichiers (images, documents de traitement de texte, etc.) binaires. Je sais que c'est juste un et des zéros sous le capot :)
Alan Storm
1
Ah, je vois ce que vous entendez par mes termes - j'utiliserai binaire / texte à l'avenir pour éviter toute confusion. Re: the \ r \ n chose - c’est ce que je comprends, ce sont les caractères ASCII pour le retour à la ligne d’une machine à écrire (déplacer au début de la ligne) et le saut de ligne (déplacer d’une ligne vers le bas). Donc, \ r \ n est un modèle "plus précis" de la réalité physique réelle à laquelle était destiné un caractère de fin de ligne. Avant OS X, les Mac n’utilisaient que cela. J'écris d'habitude le tout comme "des choix arbitraires faits dans une précipitation à laquelle nous sommes toujours confrontés"
Alan Storm

Réponses:

20

J'utiliserais fileet acheminerais la sortie dans grep ou awk pour trouver des fichiers texte, puis j'extraireais seulement la partie nomfichier de filela sortie de et acheminerais cela 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 voudrez probablement pas vous mêler des documents Rich Text, des fichiers texte Unicode, etc.

Vous pouvez également utiliser find(ou autre chose) 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

L' -d'\n'argument de xargs fait en sorte que xargs traite chaque ligne d'entrée comme un argument séparé, ce qui permet de gérer les noms de fichiers avec des espaces et d'autres caractères problématiques. c'est-à-dire que c'est une alternative au cas xargs -0où la source d'entrée ne génère pas ou ne peut pas générer de sortie séparée par NULL (telle que findl' -print0option de). Selon le journal des modifications, xargs a obtenu l' option -d/ --delimiteren septembre 2005, donc il devrait figurer dans toute distribution non-ancienne de Linux (je n'étais pas sûr, c'est pourquoi j'ai vérifié - je me souvenais vaguement qu'il s'agissait d'un ajout "récent").

Notez qu'un saut de ligne est un caractère valide dans les noms de fichiers, il sera donc cassé si un nom de fichier contient un saut de ligne. Pour les utilisateurs Unix typiques, ceci est pathologiquement insensé, mais n’est pas inconnu si les fichiers proviennent d’ordinateurs Mac ou Windows.

Notez également que ce filen'est pas parfait. Il est très efficace pour détecter le type de données dans un fichier mais peut parfois être confus.

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

cas
la source
1
Merci pour cette solution! Pour une raison fileaffiche English textplutôt que ASCII textsur mon système Solaris, donc je modifié cette partie en conséquence. Aussi, j'ai remplacé awk -F: '{print $1}'par l'équivalent cut -f1 -d:.
Andrew Cheong
3
la peine de dire grep -Ifiltres binaires
xenoterracide
Chercher le mot textdevrait suffire. Cela permettra également de prendre des filedescriptions comme ASCII Java program textou HTML document textou troff or preprocessor input text.
user1024
Ma réponse est en partie une réponse / amélioration sur cette réponse. Très bon point sur grepping pour ASCII textéviter de gâcher les RTF.
Wildcard
1
xenoterracide: Tu m'as sauvé la vie, homme! Juste un drapeau -I et BINGO
Sergio Abreu
9

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

Maintenant, une fois que vous avez ignoré cela ...

fichiers zip

S'il provient de votre utilisateur Windows sous forme de fichier zip, le format zip prend en charge le marquage des fichiers en tant que fichiers binaires ou texte dans l'archive elle-même. Vous pouvez utiliser l' -aoption de décompression pour y faire attention et convertir. Bien sûr, voyez le premier paragraphe pour savoir pourquoi cela n’est peut-être pas une bonne idée (le programme zip a peut-être mal compris lorsqu’il a créé l’archive).

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

autres fichiers

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

derobert
la source
6

Une solution générale pour traiter uniquement les fichiers non-binaires en bashutilisant 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 de fichiers et il a ajouté un -00paramètre astucieux dans la version 5.26 (publiée le 2016-04-16, par exemple dans Arch et Ubuntu 16.10 actuels) qui imprime file\0result\0plusieurs fichiers en même temps, ce qui vous permet de le faire 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 awkpartie consiste à filtrer tous les fichiers non binaires. ORSConstitue le séparateur de sortie.)

Peut aussi être utilisé en 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 ceci et de la précédente, j'ai créé un petit bashscript pour filtrer les fichiers binaires qui utilise la nouvelle méthode en utilisant le -00paramètre de filedans les versions les plus récentes et qui revient à la méthode précédente sur les versions antérieures:

#!/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 un support pour 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
la source
6

La réponse acceptée ne les a pas toutes trouvées pour moi. Voici un exemple d'utilisation de grep -Ipour ignorer les fichiers 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/112190

Phyatt
la source
4

La réponse de Cas est bonne, mais elle suppose des noms de fichiers sains ; en particulier, on suppose que les noms de fichiers ne contiendront pas de nouvelles lignes.

Il n’ya pas de bonne raison de faire cette hypothèse ici, car il est assez simple (et en fait plus propre à mon avis) de traiter cette affaire correctement aussi:

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

La findcommande utilise uniquement les fonctionnalités spécifiées par POSIX . Utiliser -execdes 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 filecela ne garantit pas l'exactitude des résultats renvoyés. Cependant, en pratique, l’obtention de "texte ASCII" dans la sortie est assez fiable.

(Il pourrait manquer certains fichiers texte peut - être, mais il est très peu probable d'identifier correctement un fichier binaire comme « texte ASCII » et mutiler it-nous donc pécher par excès de prudence.)

Wildcard
la source
Un fichier sans argument callspeut être assez lent, par exemple pour les vidéos, il vous expliquera tout sur l'encodage.
phk
Aussi, vous supposez qu'aucun fichier ne commence par -.
phk
Et je ne vois aucune raison pour laquelle vous ne feriez pas un appel unique file, cela peut prendre plusieurs fichiers comme arguments.
phk
@phk, pour répondre à vos commentaires: (1) il est bon de connaître le potentiel de lenteur, mais je ne vois pas de moyen POSIX pour empêcher cela; (2) Je ne fais aucune hypothèse sur les noms de fichier, car la findcommande préfixera ./tout nom de fichier transmis à la commande shell; (3) Utiliser grepun test sur une filesortie de commande unique à la fois est la seule façon POSIX que je puisse voir pour garantir un traitement correct des noms de fichiers pouvant contenir des nouvelles lignes.
Wildcard
J'ai examiné votre solution finale "POSIX-y" et je pense que c'est intelligent - mais vous supposez que cela fileprend en charge le --mime-encodingdrapeau et le --séparateur, qui ne sont aucunement garantis par POSIX .
Wildcard
2
find . -type f -exec grep -I -q . {} \; -print

Ceci trouvera tous les fichiers normaux ( -type f) dans le répertoire courant (ou en dessous) qui greppense être non vides et non binaires.

Il utilise grep -Ipour distinguer les fichiers binaires et non binaires. L' -Iindicateur et entraînera grepà quitter avec un statut de sortie différent de zéro lorsqu'il détectera qu'un fichier est binaire. Selon le cas grep, un fichier "binaire" est un fichier contenant des caractères en dehors de la plage imprimable ASCII.

L' -qoption to le grepfera quitter avec un statut 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 correspond à n’importe quel caractère.

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

Si vous vous sentez courageux, vous pouvez également vous y connecter flip -u:

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

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+ Vavant Tab. Dans un éditeur, il ne devrait y avoir aucun problème.

  • '[^<tab><space>-~]'correspond à tout caractère qui n'est pas du texte ASCII (les retours à la ligne sont ignorés par grep).
  • -L imprimera uniquement le nom de fichier des fichiers qui ne correspondent pas
  • -Zaffichera les noms de fichiers séparés par un caractère nul (pour xargs -0)
Vouze
la source
Il est à noter que Regex grep -P(si disponible) \test disponible avec Perl . Vous pouvez également utiliser la traduction des paramètres régionaux si le shell le prend en charge: $'\t'( bashet zshfaire).
phk
1

Solution alternative:

La commande dos2unix convertit les fins de ligne de Windows CRLF en Unix LF et ignore automatiquement les fichiers binaires. Je l'applique de manière récursive en utilisant:

find . -type f -exec dos2unix {} \;
Étincelle
la source
Depuis dos2unixpeut prendre plusieurs noms de fichiers comme argument, il est beaucoup plus efficace de le fairefind . -type f -exec dos2unix {} +
Anthon
0

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

i. (-type f -and -path '* / git / *' -iname 'README'): recherche les fichiers dans un chemin contenant le nom git et le fichier portant le nom README. Si vous connaissez un dossier spécifique et un nom de fichier à rechercher, il sera utile.

La commande ii.-exec exécute une commande sur le nom du 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.

Les commandes v.Multiple peuvent être exécutées ultérieurement. En ajoutant -exec "commande" \; 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

alpha_989
la source