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?
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.
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é ...
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.
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/*
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
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
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 {} \;
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.)
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
)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 {} \;
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