Comment inclure un tuyau | dans ma commande linux find -exec?

220

Ça ne marche pas. Cela peut-il être fait dans find? Ou ai-je besoin de xargs?

find -name 'file_*' -follow -type f -exec zcat {} \| agrep -dEOE 'grep' \;
un gars
la source

Réponses:

145

L'interprétation du symbole de canal comme une instruction pour exécuter plusieurs processus et diriger la sortie d'un processus vers l'entrée d'un autre processus est la responsabilité du shell (/ bin / sh ou équivalent).

Dans votre exemple, vous pouvez choisir d'utiliser votre shell de niveau supérieur pour effectuer la tuyauterie comme suit:

find -name 'file_*' -follow -type f -exec zcat {} \; | agrep -dEOE 'grep'

En termes d'efficacité, ces résultats coûtent une invocation de find, de nombreuses invocations de zcat et une invocation d'agrément.

Cela entraînerait la création d'un seul processus d'accord qui traiterait toutes les sorties produites par de nombreuses invocations de zcat.

Si, pour une raison quelconque, vous souhaitez invoquer plusieurs fois agrép, vous pouvez:

find . -name 'file_*' -follow -type f \
    -printf "zcat %p | agrep -dEOE 'grep'\n" | sh

Cela construit une liste de commandes à l'aide de canaux à exécuter, puis les envoie à un nouveau shell pour qu'elles soient réellement exécutées. (Omettre le "| sh" final est une bonne façon de déboguer ou d'effectuer des exécutions à sec de lignes de commande comme celle-ci.)

En termes d'efficacité, ce résultat coûte une invocation de find, une invocation de sh, de nombreuses invocations de zcat et de nombreuses invocations de entente.

La solution la plus efficace en termes de nombre d'appels de commandes est la suggestion de Paul Tomblin:

find . -name "file_*" -follow -type f -print0 | xargs -0 zcat | agrep -dEOE 'grep'

... ce qui coûte une invocation de find, une invocation de xargs, quelques invocations de zcat et une invocation d'agrément.

Rolf W. Rasmussen
la source
1
Un autre avantage des xargs serait que vous pouvez l'accélérer encore plus avec un processeur multi-cœurs moderne, en utilisant le commutateur -P (-P 0).
flolo
Oui, le commutateur -P est en effet un bon moyen d'accélérer l'exécution en général. Malheureusement, vous courez le risque que la sortie des processus zcat parallèles soit acheminée vers un accord entrelacé, ce qui affecterait le résultat. Cet effet peut être démontré en utilisant: echo -e "1 \ n2" | xargs -P 0 -n 1 oui | uniq
Rolf W. Rasmussen
@Adam, j'ai apporté la modification que vous avez suggérée.
Paul Tomblin
pour lequel vous pouvez installer la splendide commande xjobs ( à l' origine de Solaris)
sehe
4
Une réponse plus simple et plus générale est à stackoverflow.com/a/21825690/42973 : -exec sh -c "… | … " \;.
Eric O Lebigot
279

la solution est simple: exécutez via sh

... -exec sh -c "zcat {} | agrep -dEOE 'grep' " \;
flolo
la source
17
Ce que le PO tentait d'accomplir peut être satisfait par les suggestions ci-dessus, mais c'est celle qui répond réellement à la question posée. Il y a des raisons de le faire de cette façon - exec est beaucoup plus puissant que de simplement opérer sur les fichiers retournés par find, surtout lorsqu'il est combiné avec test. Par exemple: trouver geda-gaf / -type d -exec bash -c 'DIR = {}; [[$ (find $ DIR -maxdepth 1 | xargs grep -i spice | wc -l) -ge 5]] && echo $ DIR '\; Renvoie tous les répertoires du chemin de recherche qui contiennent plus de 5 lignes au total parmi tous les fichiers de ce répertoire contenant le mot spice
swarfrat
3
Meilleure réponse. Greping l'ensemble de la sortie (comme d'autres réponses suggèrent) n'est pas la même chose que grep chaque fichier. Astuce: au lieu de sh, vous pouvez utiliser n'importe quel autre shell que vous voulez (j'ai essayé avec bash et ça fonctionne bien).
pagliuca
1
Assurez-vous de ne pas ignorer l' -coption. Sinon, vous obtiendrez un No such file or directorymessage d'erreur déroutant .
asmaier
voici un excellent remplacement ps qui utilise find avec des tuyaux à l'intérieur d'un shell exécuté: / usr / bin / find / proc -mindepth 1 -maxdepth 1 -type d -regex '. * / [0-9] +' - print -exec bash -c "cat {} / cmdline | tr '\\ 0' ''; echo" \;
parity3
1
Exemple de recherche de fichiers et de les renommer avec sed à l' aide d'une expression régulière find -type f -name '*.mdds' -exec sh -c "echo {} | sed -e 's/_[0-9]\+//g' | xargs mv {}" \;
Rostfrei
16
find . -name "file_*" -follow -type f -print0 | xargs -0 zcat | agrep -dEOE 'grep'
Paul Tomblin
la source
Espérant éviter -print et xargs pour des raisons d'efficacité. C'est peut-être vraiment mon problème: find ne peut pas gérer les commandes redirigées via -exec
someguy
Cela ne fonctionne pas avec les fichiers avec des espaces dans leurs noms; corriger, remplacer -print par -print0 et ajouter l'option -0 à xargs
Adam Rosenfield
2
@someguy - Quoi? Éviter les xargs pour des raisons d'efficacité? Appeler une instance de zcat et lui passer une liste de plusieurs fichiers est beaucoup plus efficace que d'en exécuter une nouvelle instance pour chaque fichier trouvé.
Sherm Pendley
@Adam - J'ai apporté la modification que vous avez suggérée. 99% du temps quand je fais des recherches, c'est dans mes répertoires de code source, et aucun des fichiers là-bas n'a d'espace, donc je ne m'embête pas avec print0. Maintenant, mon répertoire de documents, d'autre part, je me souviens du print0.
Paul Tomblin
10

Vous pouvez également diriger vers une whileboucle qui peut effectuer plusieurs actions sur le fichier qui findse trouve. Voici donc un pour rechercher dans les jararchives un fichier de classe java donné dans un dossier avec une grande distribution de jarfichiers

find /usr/lib/eclipse/plugins -type f -name \*.jar | while read jar; do echo $jar; jar tf $jar | fgrep IObservableList ; done

le point clé étant que la whileboucle contient plusieurs commandes référençant le nom de fichier transmis séparé par un point-virgule et ces commandes peuvent inclure des canaux. Donc, dans cet exemple, je fais écho au nom du fichier correspondant, puis j'énumère ce qui se trouve dans le filtrage d'archives pour un nom de classe donné. La sortie ressemble à:

/usr/lib/eclipse/plugins/org.eclipse.core.contenttype.source_3.4.1.R35x_v20090826-0451.jar /usr/lib/eclipse/plugins/org.eclipse.core.databinding.observable_1.2.0.M20090902-0800 .jar org / eclipse / core / databinding / observable / list / IObservableList .class /usr/lib/eclipse/plugins/org.eclipse.search.source_3.5.1.r351_v20090708-0800.jar / usr / lib / eclipse / plugins / org.eclipse.jdt.apt.core.source_3.3.202.R35x_v20091130-2300.jar /usr/lib/eclipse/plugins/org.eclipse.cvs.source_1.0.400.v201002111343.jar / usr / lib / eclipse / plugins / org.eclipse.help.appserver_3.1.400.v20090429_1800.jar

dans mon shell bash (xubuntu10.04 / xfce), il rend vraiment le nom de classe correspondant en gras car il fgrepmet en évidence la chaîne correspondante; cela rend très facile de parcourir la liste des centaines de jarfichiers qui ont été recherchés et de voir facilement toutes les correspondances.

sur windows vous pouvez faire la même chose avec:

for /R %j in (*.jar) do @echo %j & @jar tf %j | findstr IObservableList

notez que dans Windows, le séparateur de commandes est '&' not ';' et que le '@' supprime l'écho de la commande pour donner une sortie ordonnée tout comme la sortie de recherche Linux ci-dessus; bien que findstrne rende pas la chaîne correspondante en gras, vous devez donc regarder un peu plus près la sortie pour voir le nom de la classe correspondante. Il s'avère que la commande windows 'for' connaît pas mal de trucs comme la boucle dans les fichiers texte ...

prendre plaisir

simbo1905
la source
2

J'ai trouvé que l'exécution d'une commande de shell de chaîne (sh -c) fonctionne mieux, par exemple:

find -name 'file_*' -follow -type f -exec bash -c "zcat \"{}\" | agrep -dEOE 'grep'" \;
Andrew Khoury
la source
0

Si vous cherchez une alternative simple, cela peut être fait en utilisant une boucle:

for i in $(find -name 'file_*' -follow -type f);do zcat $i | agrep -dEOE 'grep');done

ou, forme plus générale et facile à comprendre:

for i in $(YOUR_FIND_COMMAND);do YOUR_EXEC_COMMAND_AND_PIPES );done

et remplacez tout {} par $ i dans YOUR_EXEC_COMMAND_AND_PIPES

Louis Gagnon
la source