commande if dans find -exec

10

J'essayais simplement de répertorier tous les répertoires et fichiers sous le répertoire actuel et d'écrire également s'ils sont des fichiers ou des répertoires avec la commande suivante:

find -exec echo `echo {} : ;if [ -f {} ]; then echo file; else echo directory;fi` \;

Je sais que c'est une commande stupide, je peux utiliser d'autres choses comme -type fou -type d, mais je veux savoir pourquoi ce morceau de code n'a pas fonctionné comme je m'y attendais. Il imprime simplement un répertoire à chacun d'eux. Par exemple, alors que la sortie de findest:

.
./dir
./dir/file

la sortie de mon code est:

. : directory
./dir : directory
./dir/file : directory

Et sortie de

echo `echo dir/file : ;if [ -f dir/file ]; then echo file; else echo directory;fi`

est

dir/file : file

Je travaille Ubuntu 14.10et utilisefind (GNU findutils) 4.4.2

Esref
la source
1
find -exec bash -c 'echo -n "{} : ";if [ -f "{}" ]; then echo file; else echo directory;fi' \;
Costas
1
Merci @Costas mais je sais qu'en utilisant bash ou sh je peux atteindre mon objectif initial, mais maintenant je veux comprendre pourquoi si se comporte différemment avec le paramètre exec.
Esref
Mettez les arguments entre guillemets "{}". Pourquoi utilisez-vous echodeux fois?
Costas
J'ai déjà essayé ça et ça donne la même sortie. trouver -exec echo echo "{}" : ;if [ -f "{}" ]; then echo file; else echo directory;fi\;
Esref

Réponses:

13

Tout d'abord, votre extrait de code exécute la commande

echo {} : ;if [ -f {} ]; then echo file; else echo directory;fi

car il a besoin de sa sortie pour évaluer la substitution de commande. Puisqu'il n'y a aucun fichier nommé {}, cela produit la sortie

{} :
directory

Ensuite , la findcommande est exécutée avec les arguments -exec, echo, {}, :, directory, donc pour chaque fichier, il affiche le nom du fichier suivi d'un espace et : directory.

Ce que vous voulez réellement faire, c'est exécuter l'extrait de shell echo {} :; …sur chaque fichier trouvé par find. Cet extrait doit être exécuté par un shell généré par find, et non par le shell qui démarre find, car il reçoit des données de findsa ligne de commande. Par conséquent, vous devez demander findà exécuter un shell:

find -exec sh -c 'echo {} : ;if [ -f {} ]; then echo file; else echo directory;fi' \;

C'est mieux, mais toujours pas juste. Cela fonctionnera avec certaines (pas toutes) findimplémentations si vos noms de fichiers ne contiennent pas de caractères spéciaux, mais comme vous interpolez le nom de fichier dans un script shell, vous autorisez les noms de fichiers à exécuter des commandes shell arbitraires, par exemple si vous avez un fichier appelé $(rm -rf /)alors la commande rm -rf /sera exécutée. Pour passer des noms de fichiers au script, passez-les comme arguments séparés.

Le premier echoimprime également une nouvelle ligne après les deux points. Utilisez echo -n(si votre shell le supporte) ou printfpour éviter cela.

find -exec sh -c 'printf "%s :" "$0"; if [ -f "$0" ]; then echo file; else echo directory; fi' {} \;

Vous pouvez utiliser -exec … {} +pour regrouper les invocations de shell, ce qui est plus rapide.

find -exec sh -c 'for x; do printf "%s :" "$x"; if [ -f "$x" ]; then echo file; else echo directory; fi; done' _ {} +
Gilles 'SO- arrête d'être méchant'
la source
3

Une autre façon d'exécuter if; then; else; fiavec find:

find |
while read p; do if [ -f "$p" ]; then echo file; else echo directory; fi; done
nordinateurs
la source
1
Solution cool. Il évite les modèles de devis complexes et les complications de sous-coque. Vous pouvez avoir précédemment défini une fonction shell et l'exécuter sur les fichiers correspondant à la condition "if".
gerlos