Pourquoi ne peut-on pas gérer l'existence d'un directory de même nom dans la destination?

mv ne peut pas déplacer un directory vers une destination avec un directory de même nom:

 $ mv fortran/ imperative_PLs/ mv: cannot move 'fortran/' to 'imperative_PLs/fortran': Directory not empty 
  1. Pourquoi mv ne fonctionne pas dans ce cas? Cela peut-il être expliqué à partir des appels système que mv appelle? (Comparez à rsync qui peut)

  2. Pourquoi mv est conçu pour ne pas fonctionner dans ce cas? Quelle est la justification ou le point?

    1. mv ne fonctionne pas dans ce cas car il n'a pas été conçu pour le faire. Les appels système sont (probablement) soit

      • Déplacer vers le même système de files: rename (à l'origine link et unlink )
      • Déplacez-vous dans les filesystems: copy récursive du file suivie d'un unlink récursif
    2. Opinion: Je pense que ce n'est pas tant qu'il a été conçu pour ne pas fonctionner, car il n'a pas été conçu pour gérer ce cas d'utilisation. Pour un outil "simple" qui a l'intention de faire une chose bien, vous devez fournir un set de commutateurs pour indiquer à mv lequel de ces paths d'action à prendre:

      • Pour cautionner avec une erreur, comme dans la mise en œuvre actuelle
      • Fusionner avec une erreur si un file existe déjà
      • Pour merge, remplacez les files cible existants

    Si l'action de fusion / rlocation est ce que vous voulez, vous pouvez l'implémenter assez facilement avec cp suivi de rm , ou en utilisant l'un des utilitaires de copy d'arborescence de files tar , pax , etc.

    mv et rsync ne sont pas des programmes similaires. En particulier, mv tente souvent de simplement renommer des objects. S'il est dans le même système de files, il ne copy pas le contenu du tout.

    Si vous n'aviez pas encore de imperative_PLs/fortran , alors mv prendrait le directory fortran existant et le renommerait à ce point dans l'arborescence.

    Mais vous avez déjà un directory (avec le contenu) à cet endroit. Comme un nom ne peut referencer qu'un seul object, le directory existant doit être supprimé ou renommé. mv suppose que vous ne voulez pas faire non plus et avorte.

    rsync copy à la place les files individuels et d'autres contenus à l'intérieur de fortran et les place dans le directory imperative_PLs/fortran existant.

    Pensez-y comme rename place, et le comportement peut sembler plus compréhensible.

    mv est en fait rename sous la couverture.

    Si vous déplacez un file vers un autre file, mv suppose que vous savez ce que vous faites et écrasez le file de destination.

    Si vous déplacez un directory vers un autre directory, mv suppose que vous souhaitez conserver le nom de base de votre directory d'origine et le créer dans le directory cible. S'il n'existe pas encore de directory avec ce nom du côté de destination ou si un directory avec ce nom existe mais est vide, l'opération réussit.

    Cependant, si le directory cible existe déjà et n'est pas vide, il ne s'agit plus d'un rename mais d'un retrait récursif de files et de directorys. rename n'est pas conçu pour le faire alors il échoue, mv ne va pas plus loin car il suppose que vous ne vouliez pas le faire et échoue aussi.

    Le message d'erreur lors du déplacement entre les filesystems est légèrement plus détaillé:

     # mv a/foo b/bar mv: inter-device move failed: 'a/foo' to 'b/bar/foo'; unable to remove target: Directory not empty 

    Il n'essaie donc pas de merge des directorys comme vous semblez vous attendre, mais plutôt de supprimer la cible avant de renommer la source; et supprimer pour les directorys ne fonctionne que lorsqu'il est vide.

    En termes de syscalls, au sein du même système de files, il suffit de rename()

     rename("a/foo", "a/bar/foo") = -1 ENOTEMPTY (Directory not empty) 

    Lorsque vous vous déplacez d'un système de files à l'autre, c'est d'abord rename() détecte ce cas et une simple tentative de rmdir() .

     rename("a/foo", "b/bar/foo") = -1 EXDEV (Invalid cross-device link) rmdir("b/bar/foo") = -1 ENOTEMPTY (Directory not empty) 

    mv pourrait faire plus d'effort mais il ne veut pas. 😉