Pourquoi find correspond-il parfois à son argument de chemin de ligne de commande?

9

Sous Linux,

cd /tmp
mkdir foo; cd foo

Maintenant, en cours d'exécution

find . -name 'foo'

ne donne aucune sortie. Alors que courir

find /tmp/foo -name 'foo'

Donne la sortie /tmp/fooqui n'a pas de sens pour moi. Quelqu'un peut-il expliquer pourquoi?

York
la source
2
trouver des recherches à l'intérieur du chemin donné inclus comme il est indiqué. Donc, si vous entrez, ./cela ne correspond pasfoo
Costas
@Costas: Je comprends cela. Ce que je ne comprends pas, c'est pourquoi cela a fait une différence si je donne le chemin absolu vers find.
York
@York Dans certaines situations, aucun comportement n'est toujours correct. Imaginez un lien symbolique avec le nom barqui pointe vers un fichier fooqui est en dehors du chemin de recherche. Cela correspond-il ou non?
Hauke ​​Laging du
1
Le .et /tmp/foone sont pas les mêmes - ce sont deux liens durs différents vers le même répertoire; find /tmp/foo/. -name 'foo'ne trouve rien non plus.
jimmij
@jimmij: quand je cours find /tmp/foo -name 'foo', je demandais à bash de trouver dans le répertoire /tmp/foo, un fichier dont le nom est "foo". Parce que le répertoire /tmp/fooest vide, il ne devrait rien avoir retourné. Je ne comprends pas pourquoi il revient /tmp/foo. D'un autre côté, quand je cours find . -name 'foo', je demandais à bash la même chose, c'est-à-dire trouver un fichier dans le répertoire courant (qui se trouvait être /tmp/foo), dont le nom est 'foo', et cela ne renvoie rien de sensé.
York

Réponses:

15

findtraverse la ou les arborescences de répertoires spécifiées et évalue l'expression donnée pour chaque fichier qu'il trouve. La traversée commence au chemin donné. Voici un résumé du find . -name foofonctionnement:

  • Premier chemin sur la ligne de commande: .
    • Le nom de base ( .) correspond-il au modèle foo? Non, alors ne fais rien.
      Il se trouve que /tmp/fooc'est un autre nom pour le même répertoire. Mais findne sait pas cela (et il n'est pas censé essayer de le découvrir).
    • Le chemin est-il un répertoire? Oui, alors parcourez-le. Énumérer les entrées dans ., et pour chaque entrée, effectuer le processus de traversée.
      • Le répertoire est vide: il ne contient aucune entrée autre que .and .., qui findne traverse pas récursivement. Le travail est donc terminé.

Et find /tmp/foo:

  • Premier chemin sur la ligne de commande: /tmp/foo
    • Le nom de base ( foo) correspond-il au modèle foo? Oui, donc la condition correspond.
      • Aucune action n'est associée à cette condition, effectuez donc l'action par défaut, qui consiste à imprimer le chemin d'accès.
    • Le chemin est-il un répertoire? Oui, alors parcourez-le. Énumérer les entrées dans /tmp/foo, et pour chaque entrée, effectuer le processus de traversée.
      • Le répertoire est vide: il ne contient aucune entrée autre que .and .., qui findne traverse pas récursivement. Le travail est donc terminé.

Il se trouve que .et /tmp/foosont le même répertoire, mais cela ne suffit pas à garantir que finda le même comportement sur les deux. La findcommande permet de distinguer les chemins d'accès au même fichier; le -nameprédicat en fait partie. find /tmp/foo -name foocorrespond au répertoire de départ ainsi qu'à tout fichier en dessous appelé foo. find . -name .ne correspond qu'au répertoire de départ ( .ne peut jamais être trouvé lors d'une traversée récursive).

Gilles 'SO- arrête d'être méchant'
la source
"il ne contient aucune entrée autre que. et .., que find ne traverse pas récursivement." Je suppose que "ne traverse pas récursivement" fait référence à "..". Si c'est exact, que signifierait "transversalement récursivement" dans le contexte de ".."?
Faheem Mitha
@FaheemMitha «ne traverse pas récursivement» s'applique aux deux .et ... (Si findparcouru .récursivement, il finirait par parcourir le répertoire encore et encore et encore et…) .et ..ne sont pas seulement des notations spéciales, ils apparaissent comme des entrées de répertoire.
Gilles 'SO- arrête d'être méchant'
5

Il n'y a pas de normalisation des arguments de ligne de commande avant l'application des tests. Ainsi, les résultats diffèrent selon le chemin utilisé (si des liens symboliques sont impliqués):

cd /tmp
mkdir foo
ln -s foo bar
find /tmp/foo -name foo
find /tmp/bar -name foo

Dans «votre cas», les deux appels donneraient le même résultat, ce qui pourrait être (plus) déroutant. Vous pouvez utiliser -mindepth 1si vous voulez que les points de départ soient ignorés (peut être non POSIX).

Hauke ​​Laging
la source
-mindepth 1travaux. Cependant, je ne comprends toujours pas pourquoi find . -name 'foo'et je me find /tmp/foo -name 'foo'comporte différemment.
York
2

(gnu) find affiche toutes les correspondances trouvées dans le chemin fourni à la commande car il commence sa comparaison avec les arguments de la ligne de commande, descendant plus profondément dans la structure de répertoires à partir de là (donc, -maxdepth 0limite les tests au niveau de base ou aux arguments de la ligne de commande uniquement, tandis que -mindepth 1ignore les arguments de la ligne de commande comme man findexpliqué). C'est la raison pour laquelle find /tmp/foo -name 'foo'donnera une correspondance même si le répertoire lui-même est vide.

find . -name 'foo'd'autre part, ne donnera aucun résultat car .(point) est un fichier spécial qui agit comme un lien dur vers le même inode que /tmp/foo- c'est comme un nom de fichier séparé (bien que spécial) et non pas un lien symbolique ou une expression qui est soumis à expansion du chemin par le shell. Par conséquent, le premier test appliqué par find aux arguments de ligne de commande dans l'exemple donné n'affichera aucune correspondance, car en .effet ne correspond pas au modèle de nom défini dans -name 'foo'. Pas /tmp/foo/.plus qu'un test de -namemodèle n'est effectué sur le nom de base du chemin uniquement (voir man find), ce qui est encore le cas ..

Bien que ce comportement ne puisse pas être attendu ou semble intuitif du point de vue de l'utilisateur (et oui, cela m'a également dérouté au début), il ne constitue pas un bogue mais correspond à la logique et aux fonctionnalités décrites dans les pages man et info de ( gnu) trouver.

Shevek
la source
0

J'ai essayé de commenter la réponse de Gilles, mais c'était trop long pour tenir dans un commentaire. Pour cette raison, je le mets en réponse à ma propre question.

L'explication de Gilles (et celle de Shevek également) est claire et logique. Le point clé ici est que non seulement findessaie de faire correspondre les noms de fichier pour les fichiers à l'intérieur des chemins donnés (récursivement), mais aussi, il essaie de faire correspondre le nom de base des chemins donnés eux-mêmes.

D'un autre côté, existe-t-il une preuve que c'est ainsi que l' findon suppose que cela fonctionne, plutôt que d'être un bug? À mon avis, il serait préférable de ne pas rendre les résultats incohérents pour find .et find ABSOLUTE-PATHparce que l'incohérence est toujours déroutante et pourrait perdre beaucoup de temps au développeur à essayer de comprendre "ce qui n'allait pas". Dans mon cas, j'écrivais un script et le chemin était emprunté à une variable. Donc, pour que mon script fonctionne correctement, je peux penser à écrire find $path/. -name 'pattern'.

Enfin, je pense qu'il est possible d'obtenir des résultats cohérents finden remplaçant toujours le .par le répertoire actuel avant de continuer.

York
la source
-1

Aucun objet n'est nommé foodans le répertoire où vous avez commencé la recherche relative.

Vous avez raison de supposer que gfindc'est bogué lorsqu'il signale l' /tmp/fooutilisation du nom absolu comme répertoire de démarrage.

Gfinda divers écarts par rapport à la norme, il semble que vous en ayez trouvé un autre. Lorsque vous aimez une solution plus conforme, je recommande sfindque cela fasse partie du schilytools.

-names'applique aux résultats de la recherche dans l'annuaire. Aucun des deux cas que vous mentionnez ne renverra une entrée foode répertoire d'une readdir()opération.

schily
la source
Je soupçonne que c'est aussi un bug. Voici la sortie de find --version: find (GNU findutils) 4.4.2 Copyright (C) 2007 Free Software Foundation, Inc. Licence GPLv3 +: GNU GPL version 3 ou ultérieure < gnu.org/licenses/gpl.html >
York
Eh bien, j'ai vérifié vos exemples de commandes avec find(certifié POSIX), gfindet sfind; le problème existe uniquement lorsque vous utilisez gfind. Sous Linux, gfindn'est pas installé sous son nom natif mais sous le nom find.
schily
3
Il est correct find /tmp/foo -name foode sortir /tmp/foo. (Cc @York) Il s'agit du comportement d'OpenBSD find, de GNU find, de BusyBox find, de Solaris findet de tout autre compatible POSIX find(«Le principal doit être évalué comme vrai si le nom de base du nom de fichier examiné correspond au modèle (…)» - lorsque le nom de fichier est celui qui est passé comme opérande de chemin, aucun readdirappel n'est impliqué).
Gilles 'SO- arrête d'être méchant'