web-dev-qa-db-fra.com

Commande Linux: Comment "ne trouver" que des fichiers texte?

Après quelques recherches sur Google, je découvre:

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

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

82
datasn.io

Je sais que c’est un vieux fil, mais j’en suis tombé par hasard et je pensais partager ma méthode que j’ai trouvée être un moyen très rapide d’utiliser find pour ne trouver que des fichiers non binaires:

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

L'option -I de grep lui dit d'ignorer immédiatement les fichiers binaires et l'option . ainsi que le -q lui permettent de faire correspondre les fichiers texte immédiatement, de sorte que tout se passe très vite. Vous pouvez changer le -print en un -print0 pour transférer dans un xargs -0 ou 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 BSD de find, telles que sous OS X, mais cela ne fait pas de mal de le garder tout le temps si vous voulez le mettre dans un alias ou quelque chose du genre.

EDIT: Comme @ruslan l’a correctement souligné, le -and peut être omis car il est impliqué.

146
crudcore

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

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

mettez-le dans votre .bashrc et lancez simplement:

findTextInAsciiFiles your_folder "needle text"

quand tu veux.


EDITpour refléter l'édition de l'OP:

si vous souhaitez supprimer les informations de mime, vous pouvez simplement ajouter une étape supplémentaire au pipeline permettant de filtrer les informations de mime. Cela devrait faire l'affaire, en prenant seulement 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
}
10
peoro

Basé sur cette SO question :

grep -rIl "needle text" my_folder

9
crayzeewulf
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 le rend un peu plus facile. 

C'est un espace sécurisé:

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

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 dernier filtre sed.

$ 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'options -e 'type' à la dernière commande grep.

MODIFIER:

Si votre version xargs prend en charge l'option -d, 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|:[^:]*$||'
2
thkala

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

  • Il ne liste 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"
    
  • Cela engendre 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,2 seconde par rapport à 4 secondes pour la solution ci-dessus (2,5 Go de données/7700 fichiers), c'est-à-dire 20x plus rapide.

En outre, personne n'a cité ag, le chercheur d'argent ou ack-grep as 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 en tant que fichiers texte). J'ai déjà eu de faux positifs en utilisant soit grep/ag/ack, il est donc préférable de répertorier les fichiers correspondants avant de les éditer.

2
fuujuhi

Voici comment je l'ai fait ...

1 . créez un petit script pour tester si un fichier est en texte brut istext:

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

2 utiliser trouver comme avant

find . -type f -exec istext {} \; -exec grep -nHi mystring {} \;
2
Robert

Une autre façon de faire ceci: 

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

Si vous voulez aussi des fichiers vides: 

#  find . |xargs file {} \; |egrep "ASCII text|empty"
1
The IT Guy

Bien que ce soit une vieille question, je pense que les informations ci-dessous vont améliorer la qualité des réponses fournies ici.

En ignorant les fichiers avec le bit exécutable set, je viens d'utiliser 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 pipes pour mélanger beaucoup de commandes, juste la puissante commande plain find.

  • Clause de non-responsabilité: ce n'est pas exactement ce que l'OP a demandé, car il ne vérifie pas si le fichier est binary ou non. Par exemple, il filtrera les fichiers bash script, qui sont text eux-mêmes mais dont le bit exécutable est défini .

Cela dit, j'espère que cela sera utile à quiconque.

1
Dr Beco

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

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

2) créer 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 :)

0
Frank Fang

Voici une version simplifiée avec une explication détaillée 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 y parvenir, nous pouvons utiliser trois commandes UNIX: find, file et grep

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

file nous 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 relier 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 plus de sens (en particulier 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 ./ = parcourir tous les fichiers de ce répertoire. La commande find imprime le nom de fichier de tout fichier correspondant à "l'expression", ou de tout ce qui suit le chemin, qui dans notre cas est le répertoire en cours ou ./.

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

-exec = cet indicateur est une option de 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 commande file renvoie une chaîne qui vous indique le type de fichier d'un fichier. Régulièrement, cela ressemblerait à ceci: file mytextfile.txt. Dans notre cas, nous souhaitons qu'il utilise le fichier examiné par la commande find. Nous avons donc mis entre accolades {} pour agir en tant que variable ou 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 find et est le signe de ponctuation à la fin de notre commande -exec. Consultez le manuel de 'find' pour plus d'explications si vous en avez besoin en exécutant man find.

| grep 'ASCII' = | est un tuyau. Pipe prend la sortie de tout ce qui est à gauche et l’utilise comme entrée de ce qui est à droite. Il prend la sortie de la commande find (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, cela retourne vrai.

NOW, l'expression à droite de find ./ renverra true lorsque la commande grep renverra true. Voila. 

0
mepler

Si vous êtes intéressé par la recherche de n'importe quel type de fichier à l'aide de leurs octets magiques à l'aide de l'utilitaire awesome file associé à la puissance de find, cela peut s'avérer 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: $@"' -- @@

Sortie:

file is ASCII: ./text.txt

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

Vous pouvez modifier la partie après && pour appeler un autre script ou effectuer un autre travail en ligne, c’est-à-dire si ce fichier contient une chaîne donnée, définissez le fichier entier ou recherchez une chaîne secondaire.

Explication:

  • find éléments qui sont des fichiers
  • Faites en sorte que xargs alimente chaque élément sous forme de ligne dans un même support bash Commande/script
  • file vérifie le type de fichier par octet magique, grep vérifie si ASCII existe. Si c'est le cas, alors après && votre commande suivante sera exécutée.
  • find affiche les résultats null séparés, cela est utile pour échapper à noms de fichiers contenant des espaces et des méta-caractères.
  • xargs, à l'aide de l'option -0, les lit null séparés, -I @@ prend chaque enregistrement et utilise comme paramètre de position/args pour bash script.
  • -- pour bash garantit que tout ce qui vient après est un argument, même si. il commence par - comme -c qui pourrait autrement être interprétéas option bash

Si vous devez rechercher des types autres qu'ASCII, remplacez simplement grep ASCII par un autre type, tel que grep "PDF document, version 1.4".

0
sdkks

Je préfère xargs

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

si vos noms de fichiers sont bizarres, utilisez les options -0:

find . -type f -print0 | xargs -0 grep -I "needle text"
0
dalore
  • grep eth0 $ (recherche/etc/-type f -exec fichier {} \; | egrep -i "text | ascii" | cut -d ':' -f1)

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

0
Gabriel G