我对符号链接的基本理解是作为一个特殊文件,该文件包含另一个文件的字符串路径。内核的VFS可以将其中的很多内容抽象出来,但是是否有任何原因导致符号链接似乎无法编辑?
换句话说:我可以编辑符号链接吗?如果没有,为什么不呢?
我确实知道,有多种替换符号链接的方法(答案部分中当前有两个替代方法),但对为什么替换似乎是处理符号链接的唯一方法的解释将很有趣。您为什么不能只更改他们指向的位置?
我对符号链接的基本理解是作为一个特殊文件,该文件包含另一个文件的字符串路径。内核的VFS可以将其中的很多内容抽象出来,但是是否有任何原因导致符号链接似乎无法编辑?
换句话说:我可以编辑符号链接吗?如果没有,为什么不呢?
我确实知道,有多种替换符号链接的方法(答案部分中当前有两个替代方法),但对为什么替换似乎是处理符号链接的唯一方法的解释将很有趣。您为什么不能只更改他们指向的位置?
Answers:
鉴于-f
只进行了无声替换,您可以使用mv -T
(-T使它工作,即使/loc.../link是目录)也可以进行原子替换:
ln -s /location/to/link linkname
# ...
ln -s /location/to/link2 newlink
mv -T newlink linkname
linkname
在整个过程中都可以访问。
linkname
不是目录的符号链接。如果在GNU或FreeBSD 上使用if -T
选项,可以避免这种情况。请注意,这样不会保留链接的权限(在重要的系统上)。mv
-h
ln -sf
如果进行编辑,则意味着要更改它指向的文件,那么可以:
$ 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
当-f
参数(--force
)传递给ln时,它将导致它在调用unlink()
系统调用之前symlink()
取自以下堆栈溢出答案。
test
的目标不是目录。否则,ln -s -f .profile test
将.profile
在该目录中创建符号链接。GNU ln
可以-T
选择避免这种情况。
符号链接需要进行原子修改。如果您在编写它们的过程中途做不到,那么它们将无法工作。符号链接的内容很小(Linux上最多4095个字符:文件路径的最大长度),因此在内核级别上编辑符号链接的一部分毫无意义。因此,内核不提供任何用于编辑符号链接的接口,仅提供用于创建新接口的接口,即symlink
系统调用(以及unlink
用于删除任何文件的通用接口)。
该symlink
系统调用只能创建一个新的符号链接,它不会删除任何现有的文件。这很烦人,但与其他创建文件的系统调用保持一致,例如open
(可以创建新文件或截断现有文件,但不能用新创建的文件替换现有文件)和mkdir
。
在外壳中,您已经发现,虽然不能用ln
命令原子地替换符号链接(ln -sf
取消链接前一个文件然后创建符号链接),但是可以通过以下操作来实现:首先在临时名称下创建一个符号链接,然后然后将其移动到位。
tmp=$(TMPDIR=$(dirname -- "$link") mktemp)
ln -sf -- "$target" "$tmp"
mv -f "$tmp" "$link"
mv -f
(如ln -sf
)无法$link
指向目录。GNU ln和mv -T
为此。mv
(重命名系统调用)将始终更改inode,$link
而ln -sfT
(unlink + symlink)可能会重复使用相同的索引。
从技术上讲,没有内置命令可以编辑现有的符号链接。只需几个简短的命令即可轻松实现。
这是我编写的一个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"
}
假设链接名称作为完成的结果而存在(过去):
ln -s /the/path/to/a/file linkname
然后,有三种方法可以更改符号链接:
-f
强制使用ln 甚至用于目录-n
(可以重新使用inode):
ln -sfn /some/new/path linkname
删除符号链接并创建一个新的符号链接(甚至用于目录):
rm linkname; ln -s /some/new/path linkname
创建一个新的符号链接,然后mv
它(即使目录也发生了原子变化):
ln -s /some/new/path newlinkname
mv -fT newlinkname linkname # linkname remains after the command