创建符号链接后,可以更改它指向的内容吗?


122

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

POSIX标准没有。Solaris 10没有。MacOS X 10.5(Leopard)没有。(我可以肯定地说,AIX和HP-UX都没有。从此Linux系统调用列表来看,Linux也没有这样的系统调用。)

有什么事吗?

(我希望答案是“否”。)


由于证明否定是很难的,所以让我们重新组织这个问题。

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


仅重新链接有什么问题?为什么不仅仅发出ln覆盖旧链接的命令(或等效于API)?你有什么问题
S.Lott

9
有趣-我在问是否有系统调用来完成编程工作,并且该问题被标记为“属于其他站点”。
乔纳森·莱夫勒

3
有趣-绝对不清楚您在寻找系统调用,只是编辑了问题以添加此详细信息。那么,您怎么能期望人们甚至在写东西之前就将其扣除?
Pascal Thivent 09年

2
@ S.Lott:我正在写一篇有关安全性和符号链接的论文。有一次我断言“符号链接本身的实际所有者,组,权限是无关紧要的”,其理由是符号链接的所有者只能删除它,而不能更改其值。我正在仔细检查,除了删除实现“重写符号链接值”效果的符号链接之外,别无其他方法。我忽略了对原始磁盘的直接访问,并以此方式破解了FS-它需要root特权,而我所关心的是非root用户,而不是root可以做什么。
乔纳森·莱夫勒

4
@Pascal:对不起-我不知道我是否在谈论系统调用,直到人们偏离了我的意图(这显然与我所说的不符)。对不起,我误导了。这是无意的。
乔纳森·莱夫勒

Answers:


106

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

编辑:正如OP在注释中指出的那样,使用该--force选项将使lnunlink()before 的系统调用symlink()。下面,strace在我的linux盒子上的输出证明了这一点:

$ 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

因此,我想最后的答案是“否”。

编辑:以下是从Arto Bendiken的答案复制过来的,大约在2016年unix.stackexchange.com上。

的确可以用原子做rename(2),由下一个临时的名字第一次创建新的符号链接,然后干净利落地覆盖旧的符号链接一气呵成。如手册页所述:

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

在外壳程序中,您可以mv -T按照以下步骤进行操作:

$ mkdir a b
$ ln -s a z
$ ln -s b z.new
$ mv -T z.new z

您可以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

请注意,以上mv -Tstrace都特定于Linux。

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

编者注:这就是Capistrano从〜2.15开始多年来所做的。请参阅此拉取请求


2
'-f'选项不是强制'ln'进行unlink()然后执行symlink()系统调用吗?
乔纳森·莱夫勒

1
是的 但是这个问题可能被认为是“我应该一步一步做到”,然后归结为“步骤”的定义-命令行?系统调用?
Michael Krelin-黑客

1
我刚刚注意到您在问题中添加了系统调用文字;-)
Michael Krelin-黑客

2
@Pascal:是的。在Solaris上,“ truss ln -spx”和“ truss ln -s -fpx”的输出在第二种情况下在symlink()调用之前显示unlink()调用(在第一种情况下显示symlink()调用失败)。我希望这适用于Unix的大多数(如果不是全部)变体。
乔纳森·莱夫勒

12
+1到@Taai注释-如果要处理引用(指向)目录的符号链接,则需要指定“ -n”以确保此技巧有效。
wally 2014年

161

是的你可以!

$ ln -sfn source_file_or_directory_name softlink_name

9
感谢您的回应。该-f选项的意思是在创建新目的地之前“删除现有目的地”。因此,此命令可以达到结果,但要unlink(2)紧接着执行symlink(2)。这不是最初的问题。还要注意,被接受的答案和投票次数第二多的答案都可以ln -sf用来完成这项工作,但是如strace输出所示,它确实可以做到,unlink()而且symlink()因为没有系统调用来修改符号链接。
乔纳森·勒夫勒

17
当原始链接转到目录而不是文件时,似乎需要-n。
2014年

11
“ n”开关很重要。我为符号链接未更新但命令未在现有链接中创建另一个链接而苦恼,因为未设置'no derefencing'选项。
乔纳森·格鲁伯

14

不必显式取消链接旧的符号链接。你可以这样做:

ln -s newtarget temp
mv temp mylink

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

在某些文件系统上,符号链接的目标如果足够短,则存储在索引节点本身中(代替阻止列表)。这是在创建时确定的。

关于实际所有者和组是无关紧要的断言,Linux上的symlink(7)表示在某些情况下它很重要:

可以使用lchown(2)更改现有符号链接的所有者和组。仅在设置了粘性位的目录中删除或重命名链接时,符号链接的所有权才重要(请参阅stat(2))。

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

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


@ mark4o:很好-在sticky-it目录中引用lchown()和所有权很有用-谢谢。我知道lchown(),这对我的论文来说并不重要。我也知道粘性位目录;我认为所有权几乎是规则的标准结果-区别以及大概的原因是,通常可以删除文件(如果可以写入),并且名义上任何人都可以写入符号链接,因为777权限,但在这种情况下,规则略有不同。
乔纳森·莱夫勒

1
@ mark4o:不太好-所显示的命令序列与您认为的并不一样(至少在方案中:)set -x -e; mkdir junk; ( cd junk; mkdir olddir newdir; ln -s olddir mylink; ls -ilR; ln -s newdir temp; ls -ilR; mv temp mylink; ls -ilR; ); rm -fr junk。如果创建文件oldfile和newfile而不是目录,然后运行等效的脚本(主要是将olddir更改为oldfile,将newdir更改为newfile),则将获得预期的效果。只是一种额外的复杂性!感谢您的答复。
乔纳森·莱夫勒

2

只是对以上正确答案的警告:

如果混合使用源和目标,则使用-f / --force方法可能会丢失文件:

mbucher@server2:~/test$ ls -la
total 11448
drwxr-xr-x  2 mbucher www-data    4096 May 25 15:27 .
drwxr-xr-x 18 mbucher www-data    4096 May 25 15:13 ..
-rw-r--r--  1 mbucher www-data 4109466 May 25 15:26 data.tar.gz
-rw-r--r--  1 mbucher www-data 7582480 May 25 15:27 otherdata.tar.gz
lrwxrwxrwx  1 mbucher www-data      11 May 25 15:26 thesymlink -> data.tar.gz
mbucher@server2:~/test$ 
mbucher@server2:~/test$ ln -s -f thesymlink otherdata.tar.gz 
mbucher@server2:~/test$ 
mbucher@server2:~/test$ ls -la
total 4028
drwxr-xr-x  2 mbucher www-data    4096 May 25 15:28 .
drwxr-xr-x 18 mbucher www-data    4096 May 25 15:13 ..
-rw-r--r--  1 mbucher www-data 4109466 May 25 15:26 data.tar.gz
lrwxrwxrwx  1 mbucher www-data      10 May 25 15:28 otherdata.tar.gz -> thesymlink
lrwxrwxrwx  1 mbucher www-data      11 May 25 15:26 thesymlink -> data.tar.gz

当然这是有意的,但通常会发生错误。因此,删除和重建符号链接需要做更多的工作,但同时也可以节省一些时间:

mbucher@server2:~/test$ rm thesymlink && ln -s thesymlink otherdata.tar.gz 
ln: creating symbolic link `otherdata.tar.gz': File exists

至少保留了我的文件。


使用-for --force选项总是有点危险;假设用户知道他们的意思。如果您习惯使用它,将会双重致命(rm -fr …每次都是危险的)。
乔纳森·勒夫勒

1

还是不断开链接并创建新的链接,最终还是会做同样的事情吗?


2
为什么要先取消链接?为什么不简单地覆盖它呢?
S.Lott

5
最终结果大致相同-但所有者,组和上次修改时间(可能还有inode数量)通常都不同。
乔纳森·莱夫勒

2
@ S.Lott:“覆盖”是什么系统调用?在POSIX中,没有这样的调用。在Solaris和MacOS X中,没有这样的调用。是否可以在Linux,AIX,HP-UX以及其他类似Unix的操作系统上进行调用?Windows确实没有以相同的方式(AFAICT)具有符号链接,因此它对我的分析并不重要-但是有关等效Windows API的信息将很有用。
乔纳森·莱夫勒

@Jonathan Leffler:嗯...。该ln --force命令将绝对覆盖现有链接。
S.Lott

伙计们,我认为您是出于不同的目的。S.Lott正在讨论可执行文件ln (1),而matt b。正在讨论的事实symlink (2)不能支持覆盖。你们俩都是对的,我建议您看一下实现,ln (1)将为完成unlink-relink过程提供最理想的方法。
dmckee ---前主持人小猫,

0

以防万一它有帮助:有一种方法可以使用午夜指挥官(mc)编辑符号链接。菜单命令是(在我的mc界面上为法语):

Fichier / Éditer le lien symbolique

可以翻译成:

File / Edit symbolic link

快捷方式是Cx Cs

ln --force我不知道它可能在内部使用命令。

现在,我试图找到一种方法来一次编辑很多符号链接(这就是我到达这里的方式)。


1
您是否已验证MC在后台执行的操作?我很奇怪,实际上它unlink()后面跟有一个symlink(),仅仅是因为这是类Unix系统提供的。
乔纳森·勒夫勒

不,我没有检查。是的,事实上,它很可能像您所说的那样进行。我编辑文本框的事实使我觉得我实际上是在编辑符号链接目标,但我可能被愚弄了……
Pierre

0

从技术上讲,没有内置命令可以编辑现有的符号链接。只需几个简短的命令即可轻松实现。

这是我编写的用于更新现有符号链接的bash / zsh功能

# -----------------------------------------
# Edit an existing symbolic link
#
# @1 = Name of symbolic link to edit
# @2 = Full destination path to update existing symlink with 
# -----------------------------------------
function edit-symlink () {
    if [ -z "$1" ]; then
        echo "Name of symbolic link you would like to edit:"
        read LINK
    else
        LINK="$1"
    fi
    LINKTMP="$LINK-tmp"
    if [ -z "$2" ]; then
        echo "Full destination path to update existing symlink with:"
        read DEST
    else
        DEST="$2"
    fi
    ln -s $DEST $LINKTMP
    rm $LINK
    mv $LINKTMP $LINK
    printf "Updated $LINK to point to new destination -> $DEST"
}

2
谢谢您的努力。这不符合我成为C语言系统调用的要求。只要您对包含链接的目录具有写许可权(需要这样做),就有多种更改链接的方法。有没有办法,我知道的,即使是现在,要改变一个符号链接的价值而不做unlink(),并symlink()与新的价值,这是发生的事情与你的代码在幕后。就我个人而言,我会让函数坚持两个(或两个的倍数)参数,如果调用不正确,则保释。不过,这是一个特殊的观点。
乔纳森·勒夫勒
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.