Нет необходимости в каких-либо забавных сценариях оболочки, rsync
или специальных опциях GNU для cp
. Все просто:
$ mv target link
Если мы не знаем цель, мы подставляем выражение $(readlink link)
для target
выше:
$ mv $(readlink link) link
Утилита mv
соответствует системному вызову rename
в POSIX-подобных системах (по крайней мере, когда она не работает на разных томах файловой системы). Системный вызов rename
удаляет целевой объект, если он существует, за одну операцию.
Мы можем просто mv
файл назначения на символическую ссылку:
$ touch target
$ ln -sf target link
$ ls -l target link
lrwxrwxrwx 1 kaz kaz 6 Sep 7 2016 link -> target
-rw-rw-r-- 1 kaz kaz 0 Sep 7 2016 target
$ mv target link
$ ls -l target link
ls: cannot access target: No such file or directory
-rw-rw-r-- 1 kaz kaz 0 Sep 7 2016 link
Если мы сделаем это для разных томов, это будет смоделировано. Сначала GNU Coreutils mv
пытается выполнить системный вызов rename
. Когда это не удается, он использует unlink
для удаления целевой символической ссылки и выполняет копию:
$ touch /tmp/target
$ ln -sf /tmp/target link
$ ls -l /tmp/target link
lrwxrwxrwx 1 kaz kaz 11 Sep 7 2016 link -> /tmp/target
-rw-rw-r-- 1 kaz kaz 0 Sep 7 16:20 /tmp/target
$ strace mv /tmp/target link
[ ... snip ... ]
stat("link", {st_mode=S_IFREG|0664, st_size=0, ...}) = 0
lstat("/tmp/target", {st_mode=S_IFREG|0664, st_size=0, ...}) = 0
lstat("link", {st_mode=S_IFLNK|0777, st_size=11, ...}) = 0
rename("/tmp/target", "link") = -1 EXDEV (Invalid cross-device link)
unlink("link") = 0
open("/tmp/target", O_RDONLY|O_NOFOLLOW) = 3
fstat(3, {st_mode=S_IFREG|0664, st_size=0, ...}) = 0
open("link", O_WRONLY|O_CREAT|O_EXCL, 0600) = 4
fstat(4, {st_mode=S_IFREG|0600, st_size=0, ...}) = 0
fadvise64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0
read(3, "", 65536) = 0
[ ... ]
close(0) = 0
close(1) = 0
exit_group(0) = ?
+++ exited with 0 +++
$ ls -l /tmp/target link
ls: cannot access /tmp/target: No such file or directory
-rw-rw-r-- 1 kaz kaz 0 Sep 7 16:20 link