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
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)
Pourquoi mv
est conçu pour ne pas fonctionner dans ce cas? Quelle est la justification ou le point?
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
rename
(à l'origine link
et unlink
) unlink
récursif 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:
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. 😉