Utilisation du point-virgule (;) vs plus (+) avec exec dans find

159

Pourquoi y a-t-il une différence de sortie entre l'utilisation

find . -exec ls '{}' \+

et

find . -exec ls '{}' \;

J'ai eu:

$ find . -exec ls  \{\} \+
./file1  ./file2

.:
file1  file2  testdir1

./testdir1:
testdir2

./testdir1/testdir2:


$ find . -exec ls  \{\} \;
file1  file2  testdir1
testdir2
./file2
./file1
Ankur Agarwal
la source

Réponses:

249

Cela pourrait être mieux illustré par un exemple. Disons que cela findaffiche ces fichiers:

file1
file2
file3

Utiliser -execavec un point-virgule ( find . -exec ls '{}' \;), exécutera

ls file1
ls file2
ls file3

Mais si vous utilisez un signe plus à la place ( find . -exec ls '{}' \+), autant de noms de fichiers que possible sont passés en arguments à une seule commande:

ls file1 file2 file3

Le nombre de noms de fichiers n'est limité que par la longueur maximale de la ligne de commande du système. Si la commande dépasse cette longueur, la commande sera appelée plusieurs fois.

Martin
la source
1
Merci. ceci est très utile pour vouloir trier les fichiers résultants: find -maxdepth 1 -type f -mtime -1 -exec ls -ltr {} \ +
fbas
1
Dumb q: Je remarque que +associé à -execest toujours échappé, mais +associé à -mtimene l'est pas. Tu connais la raison? Je suppose que c'est l'habitude de s'échapper ;associée à -exec.
kevinarpe
3
@kevinarpe en effet, je le ferais à partir de l'habitude ;. Je ne peux pas imaginer qu'il soit jamais nécessaire de s'échapper+
Martin
36

Toutes les réponses à ce jour sont correctes. Je propose cela comme une démonstration plus claire (pour moi) du comportement qui est décrit en utilisant echoplutôt que ls:

Avec un point-virgule, la commande echoest appelée une fois par fichier (ou autre objet du système de fichiers) trouvé:

$ find . -name 'test*' -exec echo {} \;
./test.c
./test.cpp
./test.new
./test.php
./test.py
./test.sh

Avec un plus, la commande echon'est appelée qu'une seule fois. Chaque fichier trouvé est passé en argument.

$ find . -name 'test*' -exec echo {} \+
./test.c ./test.cpp ./test.new ./test.php ./test.py ./test.sh

Si findvous obtenez un grand nombre de résultats, vous constaterez peut-être que la commande appelée étouffe le nombre d'arguments.

Johnsyweb
la source
1
Ne devrait-il pas trouver d'ajouter les résultats uniquement à un nombre qui permet de le transmettre en toute sécurité au shell? Du moins est ce que xargsfaire ... en principe, il ne s'étouffe jamais pour trop d'arguments.
Rmano
1
@Rmano: J'ai vu find(et xargs) sur Solaris émettre plus d'arguments que ce qui pourrait être consommé. Les xargs(et find) dans les findutils de GNU semblent se comporter de manière plus raisonnable, mais tout le monde n'utilise pas GNU.
Johnsyweb
2
@Johnsyweb, tous les POSIX findessaieraient d'éviter d'atteindre la limite du nombre d'arguments. Et cela inclut celui de Solaris (10 au moins). Là où cela peut échouer, c'est si vous faites quelque chose comme find ... -exec ksh -c 'cmd "$@" "$@"' sh {} +ou find ... -exec ksh -c 'files="$*" cmd "$@"' sh {} +, mais que findvous ne pouvez pas vraiment être blâmé pour cela. Notez que GNU findétait l'une des dernières findimplémentations à prendre en charge +(autrefois, il était difficile de porter un script sur les systèmes GNU).
Stephane Chazelas
19

De man find:

commande -exec;

Exécuter la commande; true si l'état 0 est renvoyé. Tous les arguments suivants à rechercher sont considérés comme des arguments de la commande jusqu'à ce qu'un argument consistant en ';' est rencontré. La chaîne '{}' est remplacée par le nom du fichier en cours de traitement partout où il apparaît dans les arguments de la commande, pas seulement dans les arguments où il est seul, comme dans certaines versions de find. Ces deux constructions peuvent avoir besoin d'être échappées (avec un '\') ou entre guillemets pour les protéger de l'expansion par le shell. Voir la section EXEMPLES sec pour des exemples d'utilisation de l'option '-exec'. La commande spécifiée est exécutée une fois pour chaque fichier correspondant. La commande est exécutée dans le répertoire de départ. Il existe des problèmes de sécurité inévitables entourant l'utilisation de l'option -exec;

-exec commande {} +

Cette variante de l'option -exec exécute la commande spécifiée sur les fichiers sélectionnés, mais la ligne de commande est construite en ajoutant chaque nom de fichier sélectionné à la fin ; le nombre total d'appels de la commande sera bien inférieur au nombre de fichiers correspondants. La ligne de commande est construite à peu près de la même manière que xargs construit ses lignes de commande. Une seule instance de '{}' est autorisée dans la commande. La commande est exécutée dans le répertoire de départ.

Donc, d'après ce que je comprends, \;exécute une commande distincte pour chaque fichier trouvé par find, tandis que \+ajoute les fichiers et exécute une seule commande sur chacun d'eux. Le \est un caractère d'échappement, donc c'est:

ls testdir1; ls testdir2 

contre

ls testdir1 testdir2

Faire ce qui précède dans mon shell reflétait le résultat de votre question.

exemple de quand vous voudriez utiliser \+

Supposons deux fichiers, 1.tmpet 2.tmp:

1.tmp:

1
2
3

2.tmp:

0
2
3

Avec \;:

 find *.tmp -exec diff {} \;
> diff: missing operand after `1.tmp'
> diff: Try `diff --help' for more information.
> diff: missing operand after `2.tmp'
> diff: Try `diff --help' for more information.

Alors que si vous utilisez \+(pour concaténer les résultats de find):

find *.tmp -exec diff {} \+
1c1,3
< 1
---
> 0
> 2
> 30

Donc, dans ce cas, c'est la différence entre diff 1.tmp; diff 2.tmpetdiff 1.tmp 2.tmp

Il y a des cas où cela \;est approprié et \+sera nécessaire. L'utilisation de \+with en rmest une, où si vous supprimez un grand nombre de fichiers, les performances (vitesse) seront supérieures à \;.

matchew
la source
Je peux aussi lire la page de manuel. Et je l'ai fait, mais je ne pense pas comprendre la différence entre l'utilisation; vs +
Ankur Agarwal
Je ne pense pas que le -1 était juste, j'ai expliqué ma compréhension de l'homme. Je n'ai pas simplement copié l'homme et je suis parti. mais j'ai édité ma réponse pour inclure un meilleur exemple.
matchew
10

finda une syntaxe spéciale. Vous utilisez les {}tels quels parce qu'ils ont un sens à rechercher comme chemin d'accès du fichier trouvé et (la plupart) les shells ne les interprètent pas autrement. Vous avez besoin de la barre oblique inverse \;car le point-virgule a un sens pour le shell, qui le mange avant de findpouvoir l'obtenir. Donc, ce que findveut voir APRES que le shell est fait, dans la liste d'arguments passée au programme C,

"-exec", "rm", "{}", ";"

mais vous avez besoin \;sur la ligne de commande pour obtenir un point-virgule à travers le shell pour les arguments.

Vous pouvez vous en tirer \{\}parce que l'interprétation citée par le shell \{\}est juste {}. De même, vous pouvez utiliser '{}'.

Ce que vous ne pouvez pas faire, c'est utiliser

 -exec 'rm {} ;'

parce que le shell interprète cela comme un argument,

"-exec", "rm {};"

et rm {} ;n'est pas le nom d'une commande. (Du moins à moins que quelqu'un ne vienne vraiment à bout.)

Mettre à jour

la différence est entre

$ ls file1
$ ls file2

et

$ ls file1 file2

Le +catène les noms sur une ligne de commande.

Charlie Martin
la source
1
Je comprends ce que tu dis. Je demande quelle est la différence entre l'utilisation; vs +
Ankur Agarwal
1
désolé, mais avez-vous lu attentivement ma question ou mon commentaire? Peut-être ai-je besoin de le reformuler. Pourquoi y a-t-il un o / p différent lorsque j'utilise un point-virgule avec exec dans find par rapport à quand j'utilise plus avec exec dans find?
Ankur Agarwal
2
C'est une excellente explication pour pourquoi la commande est comme ça, que la réponse acceptée ne couvre pas. Merci!
Sherwin Yu
1

La différence entre ;(point-virgule) ou +(signe plus) est la façon dont les arguments sont passés dans le paramètre -exec/ de find -execdir. Par exemple:

  • using ;exécutera plusieurs commandes (séparément pour chaque argument),

    Exemple:

    $ find /etc/rc* -exec echo Arg: {} ';'
    Arg: /etc/rc.common
    Arg: /etc/rc.common~previous
    Arg: /etc/rc.local
    Arg: /etc/rc.netboot
    

    Tous les arguments suivants findsont considérés comme des arguments de la commande.

    La chaîne {}est remplacée par le nom du fichier en cours de traitement.

  • using +exécutera le moins de commandes possible (car les arguments sont combinés ensemble). C'est très similaire au fonctionnement de la xargscommande, donc elle utilisera autant d'arguments par commande que possible pour éviter de dépasser la limite maximale d'arguments par ligne.

    Exemple:

    $ find /etc/rc* -exec echo Arg: {} '+'
    Arg: /etc/rc.common /etc/rc.common~previous /etc/rc.local /etc/rc.netboot
    

    La ligne de commande est construite en ajoutant chaque nom de fichier sélectionné à la fin.

    Une seule instance de {}est autorisée dans la commande.

Voir également:

Kenorb
la source
-5

nous essayions de trouver un dossier pour l'entretien ménager.

trouver . -exec echo {} \; la commande a fonctionné pendant la nuit à la fin aucun résultat.

trouver . -exec echo {} \ + a des résultats et n'a pris que quelques heures.

J'espère que cela t'aides.

Ron
la source
2
Cette réponse n'explique pas comment ces deux méthodes fonctionnent et en quoi les résultats qu'elles produisent diffèrent.
misko321 le