Commande Linux: comment «trouver» uniquement des fichiers texte?

100

Après quelques recherches sur Google, ce que je propose est:

find my_folder -type f -exec grep -l "needle text" {} \; -exec file {} \; | grep text

qui est très peu pratique et produit des textes inutiles tels que des informations de type mime. De meilleures solutions? J'ai beaucoup d'images et d'autres fichiers binaires dans le même dossier avec beaucoup de fichiers texte que je dois parcourir.

datasn.io
la source

Réponses:

184

Je sais que c'est un vieux fil, mais je suis tombé dessus et j'ai pensé partager ma méthode que j'ai trouvée être un moyen très rapide d'utiliser findpour ne trouver que des fichiers non binaires:

find . -type f -exec grep -Iq . {} \; -print

L' -Ioption de grep lui dit d'ignorer immédiatement les fichiers binaires et l' .option avec le -qle fera correspondre immédiatement aux fichiers texte, donc cela va très vite. Vous pouvez changer le -printen un -print0pour faire passer un xargs -0ou quelque chose si vous êtes préoccupé par les espaces (merci pour le conseil, @ lucas.werkmeister!)

De plus, le premier point n'est nécessaire que pour certaines versions de BSD find, comme sur OS X, mais cela ne fait pas de mal de l'avoir là tout le temps si vous voulez le mettre dans un alias ou quelque chose.

EDIT : Comme @ruslan l'a correctement souligné, le -andpeut être omis car il est implicite.

crudcore
la source
16
Sous Mac OS X, je dois changer cela en find . -type f -exec grep -Il "" {} \;.
Alec Jacobson
3
C'est mieux que la réponse de peoro parce que 1. cela répond en fait à la question 2. Cela ne donne pas de faux positifs 3. il est beaucoup plus performant
user123444555621
3
Vous pouvez également utiliser find -type f -exec grep -Iq . {} \; -and -printce qui a l'avantage de conserver les fichiers find; vous pouvez le remplacer -printpar un autre -execqui n'est exécuté que pour les fichiers texte. (Si vous laissez grepimprimer les noms de fichiers, vous ne pourrez pas distinguer les noms de fichiers avec des sauts de ligne.)
Lucas Werkmeister
1
@ NathanS.Watson-Haigh Cela ne devrait pas, car il devrait correspondre immédiatement aux fichiers texte. Avez-vous un cas d'utilisation spécifique que vous pouvez partager?
crudcore le
2
find . -type f -exec grep -Il . {} +est beaucoup plus rapide. L'inconvénient est qu'il ne peut pas être prolongé par un autre -execcomme le suggère @ lucas.werkmeister
Henning
11

Sur la base de cette question SO :

grep -rIl "needle text" my_folder

crayzeewulf
la source
Merci, -Ic'est une bouée de sauvetage.
Dominique
10

Pourquoi est-ce peu pratique? Si vous avez besoin de l'utiliser souvent et que vous ne voulez pas le saisir à chaque fois, définissez simplement une fonction bash pour lui:

function findTextInAsciiFiles {
    # usage: findTextInAsciiFiles DIRECTORY NEEDLE_TEXT
    find "$1" -type f -exec grep -l "$2" {} \; -exec file {} \; | grep text
}

mettez-le dans votre .bashrcpuis exécutez simplement:

findTextInAsciiFiles your_folder "needle text"

quand tu veux.


MODIFIER pour refléter la modification d'OP:

si vous voulez supprimer les informations mime, vous pouvez simplement ajouter une étape supplémentaire au pipeline qui filtre les informations mime. Cela devrait faire l'affaire, en ne prenant que ce qui vient avant :: cut -d':' -f1:

function findTextInAsciiFiles {
    # usage: findTextInAsciiFiles DIRECTORY NEEDLE_TEXT
    find "$1" -type f -exec grep -l "$2" {} \; -exec file {} \; | grep text | cut -d ':' -f1
}
peoro
la source
Je ne sais pas si "grep text" est suffisamment précis pour obtenir exactement tous les fichiers texte - je veux dire, y a-t-il des types de fichiers texte qui n'ont pas de "texte" dans la chaîne de sa description de type mime?
datasn.io
@ kavoir.com: oui. D'après le filemanuel: "Les utilisateurs doivent savoir que tous les fichiers lisibles dans un répertoire ont le mot" texte "imprimé."
peoro
2
Ne serait-il pas un peu plus intelligent de rechercher des fichiers texte avant de grepping, au lieu de grepping puis de filtrer les fichiers texte?
utilisateur inconnu
/proc/meminfo, /proc/cpuinfoetc. sont des fichiers texte, mais file /proc/meminfodit /proc/meminfo: empty. Je me demande si «vide» devrait être testé en plus de «texte», mais je ne sais pas si d'autres types pourraient également signaler «vide».
Timo Kähkönen
"Pourquoi est-ce peu pratique?" - "produit des textes inutiles". Cette réponse ne le confirme pas.
user123444555621
4
find . -type f -print0 | xargs -0 file | grep -P text | cut -d: -f1 | xargs grep -Pil "search"

Ce n'est malheureusement pas un gain de place. Mettre cela dans le script bash rend les choses un peu plus faciles.

Ceci est sans danger pour l'espace:

#!/bin/bash
#if [ ! "$1" ] ; then
    echo "Usage: $0 <search>";
    exit
fi

find . -type f -print0 \
  | xargs -0 file \
  | grep -P text \
  | cut -d: -f1 \
  | xargs -i% grep -Pil "$1" "%"
Antti Rytsölä
la source
2
Il y a quelques problèmes dans votre script: 1. Et si un fichier binaire est nommé text.bin? 2. Que faire si un nom de fichier contient un :?
thkala
3

Une autre façon de procéder:

# find . |xargs file {} \; |grep "ASCII text"

Si vous voulez aussi des fichiers vides:

#  find . |xargs file {} \; |egrep "ASCII text|empty"
Le gars de l'informatique
la source
2

Que dis-tu de ça:

$ grep -rl "needle text" my_folder | tr '\n' '\0' | xargs -r -0 file | grep -e ':[^:]*text[^:]*$' | grep -v -e 'executable'

Si vous voulez les noms de fichiers sans les types de fichiers, ajoutez simplement un sedfiltre final .

$ grep -rl "needle text" my_folder | tr '\n' '\0' | xargs -r -0 file | grep -e ':[^:]*text[^:]*$' | grep -v -e 'executable' | sed 's|:[^:]*$||'

Vous pouvez filtrer les types de fichiers inutiles en ajoutant plus d' -e 'type'options à la dernière grepcommande.

ÉDITER:

Si votre xargsversion prend en charge l' -doption, les commandes ci-dessus deviennent plus simples:

$ grep -rl "needle text" my_folder | xargs -d '\n' -r file | grep -e ':[^:]*text[^:]*$' | grep -v -e 'executable' | sed 's|:[^:]*$||'
Thkala
la source
que je suis bête. Je n'ai pas remarqué de grep récursif. d'après ce que j'ai compris, c'est en fait assez rapide même si un peu limité dans de nombreuses applications. +1 pour vous.
Antti Rytsölä
2

Voici comment je l'ai fait ...

1 . faire un petit script pour tester si un fichier est en texte brut istext:

#!/bin/bash
[[ "$(file -bi $1)" == *"file"* ]]

2. utilisez find comme avant

find . -type f -exec istext {} \; -exec grep -nHi mystring {} \;
Robert
la source
Je suppose que tu veux dire == *"text"* ]]?
utilisateur inconnu
Vous pouvez utiliser le match-operator `= ~" text "]]` à la place.
utilisateur inconnu
2

J'ai deux problèmes avec la réponse de l'histumness:

  • Il ne répertorie que les fichiers texte. Il ne les recherche pas réellement comme demandé. Pour effectuer une recherche, utilisez

    find . -type f -exec grep -Iq . {} \; -and -print0 | xargs -0 grep "needle text"
    
  • Il génère un processus grep pour chaque fichier, ce qui est très lent. Une meilleure solution est alors

    find . -type f -print0 | xargs -0 grep -IZl . | xargs -0 grep "needle text"
    

    ou simplement

    find . -type f -print0 | xargs -0 grep -I "needle text"
    

    Cela ne prend que 0,2s contre 4s pour la solution ci-dessus (2,5 Go de données / 7700 fichiers), soit 20x plus rapide .

De plus, personne n'a cité ag, le Silver Searcher ou ack-grep ¸ comme alternatives. Si l'un d'entre eux est disponible, ce sont de bien meilleures alternatives:

ag -t "needle text"    # Much faster than ack
ack -t "needle text"   # or ack-grep

En dernier lieu, méfiez - vous des faux positifs (fichiers binaires pris comme fichiers texte). J'avais déjà des faux positifs en utilisant soit grep / ag / ack, donc mieux vaut lister les fichiers correspondants avant de les éditer.

fuujuhi
la source
1

Bien que ce soit une vieille question, je pense que cette info ci-dessous ajoutera à la qualité des réponses ici.

Lorsque vous ignorez les fichiers avec le bit exécutable défini, j'utilise simplement cette commande:

find . ! -perm -111

Pour l'empêcher d'entrer récursivement dans d'autres répertoires:

find . -maxdepth 1 ! -perm -111

Pas besoin de tubes pour mélanger beaucoup de commandes, juste la puissante commande plain find .

  • Avertissement: ce n'est pas exactement ce que OP a demandé, car il ne vérifie pas si le fichier est binaire ou non. Il filtrera, par exemple, les fichiers de script bash , qui sont eux-mêmes du texte mais dont le bit exécutable est défini .

Cela dit, j'espère que cela sera utile à tout le monde.

Dr Beco
la source
0

Je le fais de cette façon: 1) comme il y a trop de fichiers (~ 30k) à rechercher, je génère la liste des fichiers texte quotidiennement pour une utilisation via crontab en utilisant la commande ci-dessous:

find /to/src/folder -type f -exec file {} \; | grep text | cut -d: -f1 > ~/.src_list &

2) créez une fonction dans .bashrc:

findex() {
    cat ~/.src_list | xargs grep "$*" 2>/dev/null
}

Ensuite, je peux utiliser la commande ci-dessous pour effectuer la recherche:

findex "needle text"

HTH :)

Frank Fang
la source
0

Je préfère xargs

find . -type f | xargs grep -I "needle text"

si vos noms de fichiers sont bizarres, recherchez en utilisant les options -0:

find . -type f -print0 | xargs -0 grep -I "needle text"
Dalore
la source
0
  • exemple bash pour rechercher le texte "eth0" dans / etc dans tous les fichiers texte / ascii

grep eth0 $ (trouver / etc / -type f -exec file {} \; | egrep -i "texte | ascii" | cut -d ':' -f1)

Gabriel G
la source
0

Voici une version simplifiée avec des explications étendues pour les débutants comme moi qui essaient d'apprendre à mettre plus d'une commande sur une ligne.

Si vous deviez écrire le problème par étapes, cela ressemblerait à ceci:

// For every file in this directory
// Check the filetype
// If it's an ASCII file, then print out the filename

Pour ce faire , on peut utiliser trois commandes UNIX: find, fileet grep.

find vérifiera tous les fichiers du répertoire.

filenous donnera le type de fichier. Dans notre cas, nous recherchons un retour de 'texte ASCII'

grep cherchera le mot-clé 'ASCII' dans la sortie de file

Alors, comment pouvons-nous les assembler en une seule ligne? Il y a plusieurs façons de le faire, mais je trouve que le faire dans l'ordre de notre pseudo-code a le plus de sens (surtout pour un débutant comme moi).

find ./ -exec file {} ";" | grep 'ASCII'

Ça a l'air compliqué, mais pas mal quand on le décompose:

find ./= parcourez tous les fichiers de ce répertoire. La findcommande imprime le nom de fichier de tout fichier qui correspond à l'expression, ou tout ce qui vient après le chemin, qui dans notre cas est le répertoire courant ou./

La chose la plus importante à comprendre est que tout ce qui suit ce premier bit sera évalué comme vrai ou faux. Si True, le nom du fichier sera imprimé. Sinon, la commande continue.

-exec= cet indicateur est une option dans la commande find qui nous permet d'utiliser le résultat d'une autre commande comme expression de recherche. C'est comme appeler une fonction dans une fonction.

file {}= la commande appelée à l'intérieur de find. La filecommande renvoie une chaîne qui vous indique le type de fichier d'un fichier. Régulièrement, il ressemblerait à ceci: file mytextfile.txt. Dans notre cas, nous voulons qu'il utilise n'importe quel fichier examiné par la findcommande, nous mettons donc les accolades {}pour agir comme une variable ou un paramètre vide. En d'autres termes, nous demandons simplement au système de générer une chaîne pour chaque fichier du répertoire.

";"= ceci est requis par findet est le signe de ponctuation à la fin de notre -execcommande. Voir le manuel pour «trouver» pour plus d'explications si vous en avez besoin en exécutant man find.

| grep 'ASCII'= |est un tuyau. Le tuyau prend la sortie de ce qui se trouve à gauche et l'utilise comme entrée pour ce qui se trouve à droite. Il prend la sortie de la findcommande (une chaîne qui est le type de fichier d'un seul fichier) et la teste pour voir si elle contient la chaîne 'ASCII'. Si c'est le cas, il renvoie vrai.

MAINTENANT, l'expression à droite de find ./retournera true lorsque la grepcommande retourne true. Voila.

mepler
la source
0

Si vous souhaitez trouver n'importe quel type de fichier par ses octets magiques en utilisant l' fileutilitaire génial combiné à la puissance de find, cela peut être utile:

$ # Let's make some test files
$ mkdir ASCII-finder
$ cd ASCII-finder
$ dd if=/dev/urandom of=binary.file bs=1M count=1
1+0 records in
1+0 records out
1048576 bytes (1.0 MB, 1.0 MiB) copied, 0.009023 s, 116 MB/s
$ file binary.file
binary.file: data
$ echo 123 > text.txt
$ # Let the magic begin
$ find -type f -print0 | \
    xargs -0 -I @@ bash -c 'file "$@" | grep ASCII &>/dev/null && echo "file is ASCII: $@"' -- @@

Production:

file is ASCII: ./text.txt

Légende: $est l'invite du shell interactif où nous entrons nos commandes

Vous pouvez modifier la partie après &&pour appeler un autre script ou faire d'autres choses en ligne également, c'est-à-dire si ce fichier contient une chaîne donnée, insérez le fichier entier ou recherchez une chaîne secondaire.

Explication:

  • find éléments qui sont des fichiers
  • Faites xargsalimenter chaque élément sous forme de ligne dans une bash commande / script de ligne
  • filevérifie le type de fichier par octet magique, grepvérifie si ASCII existe, si c'est le cas, puis après &&l'exécution de la commande suivante.
  • findimprime les résultats nullséparés, c'est bien pour échapper les noms de fichiers avec des espaces et des méta-caractères.
  • xargs, en utilisant l' -0option, les lit nullséparément, -I @@ prend chaque enregistrement et utilise comme paramètre de position / args pour bash script.
  • --car bashgarantit que tout ce qui vient après est un argument même s'il commence par -like -cqui pourrait autrement être interprété comme une option bash

Si vous avez besoin de trouver des types autres que ASCII, remplacez simplement grep ASCIIpar un autre type, commegrep "PDF document, version 1.4"

sdkks
la source
-1
find . -type f | xargs file | grep "ASCII text" | awk -F: '{print $1}'

Utilisez la commande find pour lister tous les fichiers, utilisez la commande file pour vérifier qu'il s'agit de texte (pas tar, key), enfin utilisez la commande awk pour filtrer et imprimer le résultat.

Roy Zeng
la source
-4

Que dis-tu de ça

 find . -type f|xargs grep "needle text"
Navi
la source
Cela ne cherche pas"needle text"
peoro
@Navi: l'exemple d'OP fourni ne trouve que les fichiers contenant"needl text"
peoro
3
@Navi: maintenant il ne recherche plus de fichiers texte: si un fichier binaire contient, "needle text"il serait trouvé
peoro
Pourquoi est-ce que je t'écoute même?
Navi
1
@Navi: votre one-liner ne vérifie pas les types de fichiers et a également des problèmes majeurs avec les espaces dans les noms de fichiers ...
thkala