bash - puis-je faire: trouver… -exec ceci && cela?


23

Existe-t-il un moyen de combiner logiquement deux commandes shell appelées avec find-exec ?

Par exemple, pour imprimer tous les fichiers .csv qui contiennent la chaîne foo avec son occurrence, je voudrais faire:

find . -iname \*.csv -exec grep foo {} && echo {} \;

mais bash se plaint de "l'argument manquant à '-exec'"


2
Vous pouvez utiliser 2 -execen séquence ou utiliser un seul -exec sh -c 'grep foo "$0" && printf %s\\n "$0"' {} \;.
— jw013

Cela m'a fait trébucher à plusieurs reprises: je m'attends toujours à ce que le premier argument transmis à sh(dans ce cas {}) soit $1et $0ressemble à quelque chose sh. Mais en fait, vous avez raison, le premier argument apparaît comme $0. Le fait que le premier argument soit le nom de la commande d'appel n'est qu'une convention, qui n'est pas automatiquement appliquée dans ces cas.
— dubiousjim

Peut-être devrait être fusionné avec cette question: unix.stackexchange.com/q/18077/4801
— dubiousjim

Réponses:


24

-execest un prédicat qui exécute une commande (pas un shell) et s'évalue à vrai ou faux en fonction du résultat de la commande (état de sortie nul ou non nul).

Alors:

find . -iname '*.csv' -exec grep foo {} \; -print

serait imprimer le chemin du fichier si greptrouve foo dans le fichier. Au lieu de cela, -printvous pouvez utiliser un autre -execprédicat ou tout autre prédicat

find . -iname '*.csv' -exec grep foo {} \; -exec echo {} \;

Voir aussi les opérateurs !and -ofind pour la négation et ou .

Alternativement, vous pouvez démarrer un shell comme:

find . -iname '*.csv' -exec sh -c '
   grep foo "$1" && echo "$1"' sh {} \;

Ou pour éviter d'avoir à démarrer un shell pour chaque fichier:

find . -iname '*.csv' -exec sh -c '
  for i do
    grep foo "$i" && echo "$i"
  done' sh {} +

10

Le problème auquel vous êtes confronté est que le shell analyse d'abord la ligne de commande et voit deux commandes simples séparées par l' &&opérateur:, find . -iname \*.csv -exec grep foo {}et echo {} \;. Citant &&( find . -iname \*.csv -exec grep foo {} '&&' echo {} \;) court - circuite, mais maintenant la commande exécutée par findquelque chose comme greples arguments foo, wibble.csv, &&, echoet wibble.csv. Vous devez demander findd'exécuter un shell qui interprétera l' &&opérateur:

find . -iname \*.csv -exec sh -c 'grep foo "$0" && echo "$0"' {} \;

Notez que le premier argument après sh -c SOMECOMMANDest $0non $1.

Vous pouvez enregistrer l'heure de démarrage d'un processus shell pour chaque fichier en regroupant les appels de commandes avec -exec … +. Pour faciliter le traitement, transmettez une valeur fictive $0afin d' "$@"énumérer les noms de fichier.

find . -iname \*.csv -exec sh -c 'for x in "$@"; do grep foo "$x" && echo "$x"; done' \ {} +

Si la commande shell n'est que de deux programmes séparés par &&, findpeut faire le travail par elle-même: écrire deux -execactions consécutives , et la seconde ne sera exécutée que si la première se termine avec le statut 0.

find . -iname \*.csv -exec grep foo {} \; -exec echo {} \;

(Je suppose que grepet echosont juste à des fins d'illustration, car ils -exec echopeuvent être remplacés par -printet la sortie résultante n'est de toute façon pas particulièrement utile.)


2
J'ai tendance à éviter d'utiliser "$ 0" pour cela, car il est également utilisé par le shell pour afficher des messages d'erreur. Par exemple, vous pouvez voir un ./some-file: grep: command not foundmessage d'erreur déroutant . -exec find sh -c '... "$1"' sh {} \;n'aurait pas le problème. Il y a une faute de frappe (liée) dans votre deuxième commande find.
— Stéphane Chazelas

3

Dans ce cas précis, je ferais:

find . -iname \*.csv -exec grep -l foo \{\} \;

Ou si vous avez un accusé de réception :

ack -al -G '.*\.csv' foo

Pour répondre à votre question réelle, quelque chose comme ça peut fonctionner:

find . -iname \*.csv -exec sh -c "grep foo {} && echo {}" \;


3
Cette dernière commande find est non seulement non portable mais également très dangereuse car les chemins de fichiers finissent par être évalués en tant que code shell.
— Stéphane Chazelas

Raison de plus pour essayer de l'éviter en utilisant correctement ack / grep :)
— Dennis Kaarsemaker

1
L'alternative de jw013 (donnée dans un commentaire à la question) est plus sûre à cet égard. (Je viens de remarquer que les réponses de Gilles et Stéphane utilisent la même technique.)
— dubiousjim
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.