有没有办法在不先删除符号链接的情况下进行编辑?

83
所以我创建了一个符号链接
ln -s /location/to/link linkname

现在我想要更改符号链接所指向的位置。我该怎么做?有没有办法在不先删除它的情况下进行更改?

1
我认为你在这里会得到更好的回应:http://serverfault.com/ - Joshua Partogi
10个回答

58

您可以使用不同名称创建新链接,然后将其移动以替换旧链接。

ln -s /location/to/link linkname
稍后。
ln -s /location/to/link2 newlink
mv newlink linkname
如果 newlinklinkname 在同一物理设备上,mv 应该是原子的。

1
@Andrew - http://en.wikipedia.org/wiki/Atomic_operation - martin clayton
1
我确定这在某些操作系统上可以工作,但对我来说并没有起作用,移动操作只是删除了“新建”链接而没有替换旧的链接。我仍然需要先使用rm命令。 - pstanton
@pstanton 你的意思是 mv newlink linkname 导致 newlink 文件被删除,但没有覆盖 linkname 文件?它是否默默地执行了这个操作?这似乎非常神秘。 - Kyle Strand
3
如果newlinklinkname是目录,那么mv newlink linkname会将你的newlink移动到linkname中。因此,这种方法并不完美。 - Urda
1
也许你应该使用 mv -T 而不是普通的 mv - Zaffy

30

尝试运行ln -sf new_destination linkname命令。


2
这是非原子性的。有关详细信息,请参阅我的答案。我不清楚这是否是发帖者的关注点。 - Andy Ross
13
符号链接指向目录时,这种方法也无法使用。它只会在旧的目标目录中创建一个新的符号链接。 - samxli
16
使用-n(--no-dereference)开关来解决这个问题。 - Jonathan Gruber
最好更新答案(但现在可能已经太晚了)。 - undefined

27

只需更改符号链接的目标:

# ln -sfT /path/to/new/target linkname

这是一个即时、原子性的更改。


1
什么是T选项?我在我的MacOS中没有。 - Praytic
1
-T,--no-target-directory 将 LINK_NAME 始终视为普通文件。如果您想在 macOS 上使用 GNU 版本,则可以 brew install coreutils 并使用 g 前缀访问 GNU 版本,例如 gln。这样做可以避免出现微妙的差异,从而节省很多麻烦。 - Taylor D. Edmiston
这里隐含着什么意思?作为根用户? - undefined

17
如果符号链接的目标是目录,则需要在 mv 命令中添加 -T 标志,否则它会将新符号链接移动到旧符号链接的目标目录中。
原子性地切换网站到新版本的示例:
原始设置-网站存储在 www1 目录中,虚拟主机指向 www 符号链接:
ln -s www1 www

浏览网站,查看旧版本。

将新的网站文件放入新的www2目录中。

设置指向新网站的新符号链接:

ln -s www_new www2

www 符号链接移动到新网站的目录中:
mv -T www_new www

打开网站,立即查看新版本。


1
所以显然我的NAS微内核上的mv版本不支持-T参数。有没有其他替代建议可以原子化地完成这个操作? - OcularProgrammer
尝试使用-f(强制)标志创建符号链接。这似乎有效。请参见下面的答案:ln -sf new_destination linkname - ben_nuttall

10
OS X上,ln的man页面上说你可以这样做:
ln -shf /location/to/link link name

从man页面中:
The options are as follows:
 -F    If the target file already exists and is a directory, then remove it so that the link may occur.  The -F
       option should be used with either -f or -i options.  If none is specified, -f is implied.  The -F option is
       a no-op unless -s option is specified.

 -h    If the target_file or target_dir is a symbolic link, do not follow it.  This is most useful with the -f
       option, to replace a symlink which may point to a directory.

 -f    If the target file already exists, then unlink it so that the link may occur.  (The -f option overrides any
       previous -i options.)

 -i    Cause ln to write a prompt to standard error if the target file exists.  If the response from the standard
       input begins with the character `y' or `Y', then unlink the target file so that the link may occur.  Other-
       wise, do not attempt the link.  (The -i option overrides any previous -f options.)

 -n    Same as -h, for compatibility with other ln implementations.

 -s    Create a symbolic link.

 -v    Cause ln to be verbose, showing files as they are processed.

OS X的版本是什么?默认的shell在macOS v10.15(Catalina. 2019-10-07)中已经改为Z shell,如果这对你有影响的话。 - undefined

7

对于目录,您需要执行以下操作: ln -sfT /新目标位置 旧链接名称

这是一个符号链接命令,用于创建指向新目标位置的旧链接名称的链接。


这是比必须调用第二个命令要好得多的解决方案。 - dsymquen
这不是已经在之前的回答中涵盖了吗? - undefined

5

不行。如果newpath已经存在,symlink系统调用将返回EEXIST错误。你只能从文件系统中的新节点创建符号链接。这里有什么要求?如果你担心由于unlink/symlink调用的非原子性而导致的竞争问题,那么你可能需要重新考虑架构,在其他地方提供同步。这种事情曾经引入了一些可怕的安全漏洞。


4

正如其他人所提到的,你基本上必须先手动删除符号链接,或通过向ln实用程序传递-f标志来删除。

多年前,我经常需要对符号链接进行小修改,因此我编写了一个基于readline的简单实用程序(edln),以使这个过程不那么烦人。如果有人发现它有用,我已经将其放在https://github.com/jjlin/edln/上。

edln将显示原始符号链接目标;然后您可以使用箭头键或标准readline击键(M-b、M-f、C-d等)移动和编辑目标。


谢谢!我可以在 [tag:openbsd] 上编译和运行它,但是 Makefile 缺少安装目标 :-) - user4104817
只是一条注释,至少在今天的bash中,你可以使用read内置的readline支持:read -e -p 'New: ' -i "$(readlink -- "$1")" lnk - usretc
拥有一个以bash脚本实现的edln版本会非常不错,但是通过bash似乎无法像readline一样自定义行为。我尝试过了,但是我还没有找到如何避免不良行为,比如shell转义的文件名补全。 - jjlin
(touch 'a b'; IFS= read -e -p 'New: ' -i ./ lnk; echo ":$lnk:"; ls "$lnk") 对我来说似乎是有效的, read -e 取消了转义;只有在按下 <TAB> 后才会出现尾随的空格,用户必须将其删除。 - usretc
啊...关键是不要将-r传递给read -- 这会带来所有的影响。然后,Tab自动补全会添加转义字符,这些字符会被read的默认行为撤销。 - usretc

3
将命令链式串联起来,就像这样:
rm currentlink && ln -s /path/to/link currentlink

第一条命令会删除现有的内容,第二条命令会立即重新创建它。

1

我刚刚搜索了一下,没有找到好的答案,只能自己解决:

ln -f -s -T `readlink SomeLibrary | sed 's/version.old/version.new/'` SomeLibrary

编辑的定义是部分修改而不是从头开始重新创建。任何需要记忆路径的答案,可能会很长或带有奇怪的符号,都是不好的。


“记住路径”是什么意思?为什么这是必要的?你能举个例子吗? - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接