你能改变一个符号链接创建后指向的内容吗?

Jon*_*ler 119 unix symlink

是否有任何操作系统提供一种机制(系统调用 - 而不是命令行程序)来更改符号链接(符号链接)引用的路径名 - 除了取消旧链接和创建新链接之外的路径名?

POSIX标准没有.Solaris 10没有.MacOS X 10.5(Leopard)没有.(我很可能确定AIX和HP-UX都没有.从这个Linux系统调用列表来看,Linux也没有这样的系统调用.)

有什么事吗?

(我期待答案是"不".)


由于证明负面是困难的,让我们重新组织这个问题.

如果您知道某些(类Unix)操作系统没有列出系统调用来重写符号链接的值(返回的字符串readlink())而不删除旧的符号链接并创建一个新的符号链接,请添加它 - 或者它们 -在答案中.

Taa*_*aai 158

是的你可以!

$ ln -sfn source_file_or_directory_name softlink_name
Run Code Online (Sandbox Code Playgroud)

  • 当原始链接转到目录而不是文件时,似乎需要-n. (16认同)
  • 'n'开关很重要.我一直在努力解决没有更新符号链接的问题,但是命令在现有符号中创建了另一个链接,因为没有设置'no derefencing'选项. (10认同)
  • 谢谢你的回复.`-f`选项表示在创建新目标之前'删除现有目标'.所以,这个命令实现了结果,但通过`unlink(2)`后跟`symlink(2)`.这不是原始问题的内容.还要注意接受的答案和下一个投票的答案都使用`ln -sf`来完成工作,但是当`strace`输出显示时,它确实`unlink()`和`symlink()`因为它没有'系统调用以修改符号链接. (9认同)

Pas*_*ent 100

AFAIK,不,你不能.你必须删除它并重新创建它.实际上,您可以覆盖符号链接,从而更新它引用的路径名:

$ ln -s .bashrc test
$ ls -al test
lrwxrwxrwx 1 pascal pascal 7 2009-09-23 17:12 test -> .bashrc
$ ln -s .profile test
ln: creating symbolic link `test': File exists
$ ln -s -f .profile test
$ ls -al test
lrwxrwxrwx 1 pascal pascal 8 2009-09-23 17:12 test -> .profile
Run Code Online (Sandbox Code Playgroud)

编辑:正如OP在评论中指出的那样,使用该--force选项将使之前ln执行系统调用.下面,我的linux盒子的输出证明了它:unlink()symlink()strace

$ strace -o /tmp/output.txt ln -s -f .bash_aliases test
$ grep -C3 ^unlink /tmp/output.txt 
lstat64("test", {st_mode=S_IFLNK|0777, st_size=7, ...}) = 0
stat64(".bash_aliases", {st_mode=S_IFREG|0644, st_size=2043, ...}) = 0
symlink(".bash_aliases", "test")        = -1 EEXIST (File exists)
unlink("test")                          = 0
symlink(".bash_aliases", "test")        = 0
close(0)                                = 0
close(1)                                = 0
Run Code Online (Sandbox Code Playgroud)

所以我猜最后的答案是"不".

编辑:以下是从大约2016年的unix.stackexchange.com上的Arto Bendiken的回答中复制.

可以rename(2)通过首先在临时名称下创建新符号链接,然后一次性干净地覆盖旧符号链接,以原子方式完成.正如手册页所述:

如果newpath引用符号链接,则链接将被覆盖.

在shell中,您将按mv -T如下方式执行此操作:

$ mkdir a b
$ ln -s a z
$ ln -s b z.new
$ mv -T z.new z
Run Code Online (Sandbox Code Playgroud)

您可以strace使用最后一个命令来确保它确实rename(2)在引擎盖下使用:

$ strace mv -T z.new z
lstat64("z.new", {st_mode=S_IFLNK|0777, st_size=1, ...}) = 0
lstat64("z", {st_mode=S_IFLNK|0777, st_size=1, ...}) = 0
rename("z.new", "z")                    = 0
Run Code Online (Sandbox Code Playgroud)

需要注意的是在上面的,无论是mv -Tstrace是Linux特有.

在FreeBSD上,mv -h交替使用.

编者按:这就是卡皮斯特拉诺多年来一直在做的事情,从那时起〜2.15.请参阅此拉取请求.

  • +1到@Taai评论 - 如果处理一个解除引用(指向)目录的符号链接,你需要指定"-n"以确保这个技巧有效. (11认同)
  • 所以答案是:"ln -s -f link_pointer link_name". (3认同)
  • '-f' 选项不是强制 'ln' 执行 unlink() 然后 symlink() 系统调用吗? (2认同)
  • @Pascal:确实如此。在 Solaris 上,'truss ln -spx' 和 'truss ln -s -fpx' 的输出显示了在第二种情况下 symlink() 调用之前的 unlink() 调用(第一种情况下 symlink() 调用失败)。我希望这适用于大多数(如果不是所有)Unix 变体。 (2认同)

mar*_*k4o 12

没有必要明确取消链接旧的符号链接.你可以这样做:

ln -s newtarget temp
mv temp mylink
Run Code Online (Sandbox Code Playgroud)

(或使用等效的符号链接并重命名调用).这比显式取消链接更好,因为重命名是原子的,因此可以确保链接始终指向旧目标或新目标.但是,这不会重用原始的inode.

在某些文件系统上,符号链接的目标存储在inode本身(代替阻止列表),如果它足够短; 这是在创建时确定的.

关于实际所有者和组是无关紧要的断言,Linux上的符号链接(7)表明存在重要的情况:

可以使用lchown(2)更改现有符号链接的所有者和组.符号链接的所有权唯一重要的时间是在具有粘滞位设置的目录中删除或重命名链接时(请参阅stat(2)).

可以使用utimensat(2)或lutimes(3)更改符号链接的最后访问和最后修改时间戳.

在Linux上,符号链接的权限不会在任何操作中使用; 权限始终为0777(所有用户类别的读取,写入和执行),并且无法更改.