Comment utiliser deux commandes bash dans l'option -exec de la commande find?

32

Est-il possible d'utiliser 2 commandes dans la -execpartie findcommande?

J'ai essayé quelque chose comme:

find . -name "*" -exec  chgrp -v new_group {}  ; chmod -v 770 {}  \;

et je reçois:

find: argument manquant pour -exec
chmod: ne peut pas accéder {}: aucun fichier ou répertoire de ce type
chmod: ne peut pas accéder ;: aucun fichier ou répertoire de ce type

Luc M
la source

Réponses:

44

En ce qui concerne la findcommande, vous pouvez également simplement ajouter plusieurs -execcommandes à la suite:

find . -name "*" -exec chgrp -v new_group '{}' \; -exec chmod -v 770 '{}' \;

Notez que cette commande est, dans son résultat, équivalente à utiliser

fichier chgrp -v new_group && fichier 770 de chmod -v

sur chaque fichier.

Tous les findparamètres tels que l » -name, -exec, -sizeet ainsi de suite, sont en fait des tests : findcontinuera à les exécuter un par un, tant que la chaîne jusqu'à présent a évalué à vrai . Ainsi, chaque -execcommande consécutive n’est exécutée que si les précédentes ont renvoyé la valeur true (c’est-à-dire le 0statut de sortie des commandes). Mais findcomprend également les opérateurs logiques tels que ou ( -o) et non ( !). Par conséquent, pour utiliser une chaîne de -exectests quels que soient les résultats précédents, il faudrait utiliser quelque chose comme ceci:

find . -name "*" \( -exec chgrp -v new_group {} \; -o -exec chmod -v 770 {} \; \)
rozcietrzewiacz
la source
4
+1: Oui, c'est la manière la plus élégante de le faire. Si vous pouvez expliquer pourquoi vous utilisez '{}'(apostrophes autour des accolades), rendez-
Utilisateur inconnu
1
@user Malheureusement, je ne sais pas si c'est encore nécessaire. Je viens de faire quelques tests et je ne suis pas tombé dans une situation où cela changerait quoi que ce soit. J'imagine que c'est simplement une "bonne pratique" qui va disparaître.
rozcietrzewiacz
2
Les citations sont importantes pour les fichiers avec des espaces dans leurs noms.
naught101
17
find . -name "*" -exec sh -c 'chgrp -v new_group "$0" ; chmod -v 770 "$0"' {} \;
Glenn Jackman
la source
@ Gilles: Les merveilles de l -c'étrange traitement de 0 $ me font penser que c'est faux à chaque fois que j'y jette un coup d'œil, mais c'est tout à fait correct.
derobert
J'aime la coquille explicite étant définie ...
djangofan
Cette réponse (et la réponse de Giles) semble être la meilleure réponse à la question donnée sh -c.
14

Votre commande est d'abord analysée par le shell en deux commandes séparées par un ;, ce qui équivaut à une nouvelle ligne:

find . -name "*" -exec chgrp -v new_group {}
chmod -v 770 {} \;

Si vous voulez exécuter une commande shell, appelez explicitement un shell avec bash -c(ou sh -cpeu importe que le shell soit spécifiquement bash):

find . -name "*" -exec sh -c 'chgrp -v new_group "$0"; chmod -v 770 "$0"' {} \;

Notez l'utilisation de {}comme argument pour le shell; c'est l'argument zéro (qui est normalement le nom du shell ou du script, mais peu importe ici), et donc référencé par "$0".

Vous pouvez transmettre plusieurs noms de fichiers au shell à la fois et faire en sorte que celui-ci les parcoure, ce sera plus rapide. Ici, je passe _comme nom de script et les arguments suivants sont des noms de fichiers, qui for x(un raccourci pour for x in "$@") itèrent.

find . -name "*" -exec sh -c 'for x; do chgrp -v new_group "$x"; chmod -v 770 "$x"; done' _ {} +

Notez que depuis bash 4 ou en zsh, vous n'avez pas besoin de trouver du tout ici. Dans bash, exécutez shopt -s globstar(mettez-le dans votre ~/.bashrc) pour l'activer **/pour un répertoire glob récursif. (Dans zsh, cela est actif tout le temps.) Puis

chgrp -v new_group -- **/*; chmod -v 770 -- **/*

ou si vous voulez que les fichiers soient itérés dans l'ordre

for x in **/*; do
  chgrp -v new_group -- "$x"
  chmod -v 770 -- "$x"
done

Une différence avec la findcommande est que le shell ignore les fichiers de points (fichiers dont le nom commence par a .). Pour les inclure, dans bash, premier ensemble GLOBIGNORE=.:..; dans zsh, utilisez **/*(D)comme motif glob.

Gilles, arrête de faire le mal
la source
Cette réponse (et la réponse de Glenn) semble être la meilleure réponse à la question donnée sh -c.