Comment diriger la liste des fichiers retournés par la commande find vers cat pour afficher tous les fichiers

204

Je fais un findpuis j'obtiens une liste de fichiers. Comment puis-je le diriger vers un autre utilitaire comme cat(de sorte que cat affiche le contenu de tous ces fichiers) et avoir essentiellement besoin de grepquelque chose de ces fichiers.

Devang Kamdar
la source

Réponses:

340
  1. Tuyauterie vers un autre processus (bien que cela n'accomplisse pas ce que vous avez dit que vous essayez de faire):

    command1 | command2
    

    Cela enverra la sortie de command1 comme entrée de command2

  2. -execsur un find(cela fera ce que vous voulez faire - mais est spécifique à find)

    find . -name '*.foo' -exec cat {} \;
    

    (Tout ce qui se trouve entre findet -execsont les prédicats de recherche que vous utilisiez déjà. {}Remplacera le fichier particulier que vous avez trouvé dans la commande ( cat {}dans ce cas); \;c'est pour terminer la -execcommande.)

  3. envoyer la sortie d'un processus en tant qu'arguments de ligne de commande à un autre processus

    command2 `command1`
    

    par exemple:

    cat `find . -name '*.foo' -print`
    

    (Notez que ce ne sont pas des guillemets réguliers (sous le tilde ~ de mon clavier).) Cela enverra la sortie de command1dans en command2tant qu'arguments de ligne de commande. Notez que les noms de fichiers contenant des espaces (sauts de ligne, etc.) seront cependant divisés en arguments séparés.

kenj0418
la source
2
chat find -name '*.foo' -printa très bien fonctionné pour moi ... Merci
Devang Kamdar
Les backquotes fonctionnent très bien et sont plus généralisés, vous pouvez également l'utiliser pour répertorier une liste de fichiers à partir d'un fichier.
Hazok
11
Notez que les versions modernes de findvous permettent d'écrire:, find . -name '*.foo' -exec cat {} +où le +indique que finddoit regrouper autant de noms de fichiers que possible en une seule invocation de commande. Ceci est très utile (il traite des espaces, etc. dans les noms de fichiers sans recourir à -print0et xargs -0).
Jonathan Leffler
18
Non mentionné:find . -name '*.foo' | xargs cat
stewSquared
3
Juste pour ajouter la réponse de @stewSquared: Pour trouver toutes les lignes dans les fichiers qui contiennent une certaine chaîne, faitesfind . -name '*.foo' | xargs cat | grep string
Bim
84

Version moderne

POSIX 2008 a ajouté le +marqueur, findce qui signifie qu'il regroupe désormais automatiquement autant de fichiers qu'il est raisonnable en une seule exécution de commande, tout comme le xargsfait, mais avec un certain nombre d'avantages:

  1. Vous n'avez pas à vous soucier des caractères impairs dans les noms de fichiers.
  2. Vous n'avez pas à vous soucier de l'invocation de la commande avec zéro nom de fichier.

Le problème du nom de fichier est un problème avec xargssans l' -0option, et le problème "exécuter même avec zéro nom de fichier" est un problème avec ou sans l' -0option - mais GNU xargsa l' option -rou --no-run-if-emptypour empêcher que cela ne se produise. En outre, cette notation réduit le nombre de processus, pas que vous êtes susceptible de mesurer la différence de performances. Par conséquent, vous pourriez raisonnablement écrire:

find . -exec grep something {} +

Version classique

find . -print | xargs grep something

Si vous êtes sous Linux ou avez le GNU findet les xargscommandes, utilisez -print0avec findet -0avec xargspour gérer les noms de fichiers contenant des espaces et d'autres caractères impairs.

find . -print0 | xargs -0 grep something

Ajuster les résultats de grep

Si vous ne voulez pas les noms de fichiers (juste le texte), ajoutez une option appropriée à grep(généralement -hpour supprimer les «en-têtes»). Pour garantir absolument que le nom de fichier est imprimé par grep(même si un seul fichier est trouvé ou si la dernière invocation de grepn'est donnée que 1 nom de fichier), puis ajoutez /dev/nullà la xargsligne de commande, de sorte qu'il y aura toujours au moins deux noms de fichiers.

Jonathan Leffler
la source
Pour ceux qui sont confus comme moi, notez que cette façon donnera d'abord toutes les sorties de find, puis les sorties de xargs grep something.
Eric Hu
3
@EricHu: Je peux voir que vous êtes confus, mais il ne fait pas ce que vous dites qu'il fait, du moins sur aucun système basé sur Unix que je connaisse. La sortie de findest dirigée vers l'entrée standard de xargs. Le xargsprogramme lit son entrée standard, divise l'entrée à un espace blanc (blancs, sauts de ligne, tabulations, etc.) et ajoute un certain nombre de mots à la commande grep somethinget exécute la ligne de commande. xargscontinue ensuite à lire les entrées et à exécuter les commandes jusqu'à ce qu'elles soient épuisées. xargsexécute la grepcommande aussi souvent que nécessaire pour l'entrée qui lui est donnée (à partir de finddans cet exemple).
Jonathan Leffler
Ah mon erreur, cela utilise grep pour rechercher dans chaque fichier correspondant. Je cherchais simplement à filtrer la sortie de find avec grep
Eric Hu
1
Les erreurs vont à l'erreur standard (descripteur de fichier 2) sur toutes les commandes bien comportées. Rediriger stderr vers /dev/nullperd les messages d'erreur.
Jonathan Leffler
1
C'est aussi l'avantage qu'il fonctionne mieux avec des espaces dans le chemin du fichier. Même 'sed'ing "" -> "\" le casse avec le `mais avec xargs cela fonctionne parfaitement
JZL003
36

Il existe plusieurs façons de transmettre la liste des fichiers renvoyés par la findcommande à la catcommande, bien que techniquement tous n'utilisent pas de tuyauterie et qu'aucun ne soit directement dirigé vers cat.

  1. Le plus simple est d'utiliser des backticks ( `):

    cat `find [whatever]`
    

    Cela prend la sortie de findet la place effectivement sur la ligne de commande de cat. Cela ne fonctionne pas bien s'il finda trop de sortie (plus que ce qui peut tenir sur une ligne de commande) ou si la sortie a des caractères spéciaux (comme des espaces).

  2. Dans certains shells, notamment bash, on peut utiliser à la $()place des backticks:

    cat $(find [whatever])
    

    C'est moins portable, mais est emboîtable. Mis à part cela, il a à peu près les mêmes mises en garde que les contre-coups.

  3. Parce que l'exécution d'autres commandes sur ce qui a été trouvé est une utilisation courante pour find, find a une -execaction qui exécute une commande pour chaque fichier qu'il trouve:

    find [whatever] -exec cat {} \;
    

    Le {}est un espace réservé pour le nom de fichier, et les \;marques de la fin de la commande (il est possible d'avoir d' autres actions après -exec.)

    Cela s'exécutera catune fois pour chaque fichier plutôt que d'exécuter une seule instance en lui catpassant plusieurs noms de fichiers, ce qui peut être inefficace et ne pas avoir le comportement que vous souhaitez pour certaines commandes (bien que ce soit bien pour cat). La syntaxe est également difficile à taper - vous devez échapper au point-virgule car le point-virgule est spécial pour le shell!

  4. Certaines versions de find(notamment la version GNU) vous permettent de remplacer ;par +pour utiliser -execle mode d'ajout pour exécuter moins d'instances de cat:

    find [whatever] -exec cat {} +
    

    Cela passera plusieurs noms de fichiers à chaque invocation de cat, ce qui peut être plus efficace.

    Notez cependant qu'il n'est pas garanti d'utiliser une seule invocation. Si la ligne de commande est trop longue, les arguments sont répartis sur plusieurs invocations de cat. Car catce n'est probablement pas un gros problème, mais pour certaines autres commandes, cela peut changer le comportement de manière indésirable. Sur les systèmes Linux, la limite de longueur de ligne de commande est assez grande, donc la division en plusieurs invocations est assez rare par rapport à certains autres systèmes d'exploitation.

  5. L'approche classique / portable consiste à utiliser xargs:

    find [whatever] | xargs cat
    

    xargsexécute la commande spécifiée ( cat, dans ce cas) et ajoute des arguments en fonction de ce qu'elle lit depuis stdin. Tout comme -execavec +, cela interrompra la ligne de commande si nécessaire. Autrement dit, si findproduit trop de sortie, il s'exécutera catplusieurs fois. Comme mentionné dans la section -execprécédente, il existe certaines commandes où cette séparation peut entraîner un comportement différent. Notez que l'utilisation de xargsce type a des problèmes avec les espaces dans les noms de fichiers, car xargsutilise simplement des espaces comme délimiteur.

  6. La méthode la plus robuste, portable et efficace utilise également xargs:

    find [whatever] -print0 | xargs -0 cat
    

    L' -print0indicateur indique findd'utiliser des \0délimiteurs (caractère nul) entre les noms de fichiers, et l' -0indicateur indique xargsd'attendre ces \0délimiteurs. Cela a un comportement à peu près identique à l' approche -exec... +, bien qu'il soit plus portable (mais malheureusement plus verbeux).

Laurence Gonsalves
la source
La méthode du backtick est géniale, car elle fonctionne également pour d'autres commandes ls.
Martin Braun
@Martin Braun using Fonctionne $()également avec des commandes autres que find .
Laurence Gonsalves
Merci, bon à savoir, j'ai un peu arrêté de lire après (1), car cela répond à mes besoins, car je n'ai pas affaire à des caractères spéciaux comme les espaces et autres.
Martin Braun
9

Pour ce faire (en utilisant bash), je ferais ce qui suit:

cat $(find . -name '*.foo')

Ceci est connu comme la "substitution de commande" et il supprime le saut de ligne par défaut, ce qui est vraiment pratique!

plus d'infos ici

Stéphane
la source
6

Cela me semble être un travail pour un script shell:

for file in 'find -name *.xml'
do
   grep 'hello' file
done

ou quelque chose comme ça

Gandalf
la source
2
Il s'agit d'une réponse valide, mais pas nécessairement optimale, à la question.
Jonathan Leffler
1
... ouais mais c'est super si vous voulez aussi un gros fichier avec des noms de fichiers.
ʍǝɥʇɐɯ
1
J'aime mieux ça. Un bloc de boucle comme celui-ci laisse la place à d'autres choses.
kakyo
4

Voici ma façon de trouver des noms de fichiers qui contiennent du contenu qui m'intéresse, juste une seule ligne bash qui gère également les espaces dans les noms de fichiers:

find . -name \*.xml | while read i; do grep '<?xml' "$i" >/dev/null; [ $? == 0 ] && echo $i; done
Greg
la source
3

J'utilise quelque chose comme ça:

find . -name <filename> -print0 | xargs -0 cat | grep <word2search4>

L' -print0argument " " pour "trouver" et " -0" l'argument pour "xargs" sont nécessaires pour gérer correctement les espaces dans les chemins / noms de fichiers.

ekinak
la source
2

La commande find a un argument -exec que vous pouvez utiliser pour des choses comme ça, vous pouvez simplement faire le grep directement en utilisant cela.

Par exemple (à partir d'ici, d'autres bons exemples sur cette page ):

find . -exec grep "www.athabasca" '{}' \; -print 
Chad Birch
la source
2

Voici ma photo à usage général:

grep YOURSTRING `find .`

Il imprimera le nom du fichier

zakki
la source
2

En bash, les éléments suivants seraient appropriés:

find /dir -type f -print0 | xargs -0i cat {} | grep whatever

Cela trouvera tous les fichiers dans le /dirrépertoire et xargsdirigera les noms de fichiers en toute sécurité, qui conduiront en toute sécurité grep.

Ignorer xargsn'est pas une bonne idée si vous avez plusieurs milliers de fichiers dans /dir; catse cassera en raison de la longueur excessive de la liste des arguments. xargsva tout régler pour vous.

L' -print0argument à findmailles avec l' -0argument à xargspour gérer correctement les noms de fichiers avec des espaces. L' -iargument à xargsvous permet d'insérer le nom de fichier à l'endroit requis dans la catligne de commande. Les crochets sont remplacés par le nom de fichier redirigé vers la catcommande à partir de find.

McClain Looney
la source
0

Ça marche pour moi

find _CACHE_* | while read line; do
    cat "$line" | grep "something"
done
Steven Penny
la source
0

Utilisez ggrep .

ggrep -H -R -I "mysearchstring" *

rechercher un fichier sous unix contenant du texte situé dans le répertoire courant ou un sous-répertoire

Underverse
la source
0

Cela affichera le nom et le contenu des fichiers de manière récursive uniquement.

find . -type f -printf '\n\n%p:\n' -exec cat {} \;

Modifier (version améliorée): Cela imprime le nom et le contenu des fichiers texte (ascii) de manière récursive uniquement.

find . -type f -exec grep -Iq . {} \; -print | xargs awk 'FNR==1{print FILENAME ":" $0; }'

Encore une tentative

find . -type f -exec grep -Iq . {} \; -printf "\n%p:" -exec cat {} \;
Prashant Adlinge
la source
-1

Essayez-vous de trouver du texte dans des fichiers? Vous pouvez simplement utiliser grep pour cela ...

grep searchterm *
Scott Anderson
la source
-1

Pour lister et voir le contenu de tous les fichiers abc.def sur un serveur dans les répertoires / ghi et / jkl

find /ghi /jkl -type f -name abc.def 2> /dev/null -exec ls {} \; -exec cat {} \;

Pour lister les fichiers abc.def qui ont commenté des entrées et les afficher, voyez ces entrées dans les répertoires / ghi et / jkl

find /ghi /jkl -type f -name abc.def 2> /dev/null -exec grep -H ^# {} \;
Sharjeel
la source