如何将符号链接转换为常规文件?

将符号链接转换为常规文件(即符号链接目标的副本)最直接的方法是什么?

假设filename是符号链接target 。 将其变成副本的明显过程是:

 cp filename filename-backup rm filename mv filename-backup filename 

有没有更直接的方法(即单一的命令)?

没有一个命令将符号链接转换为常规文件。 最直接的方法是使用readlink来查找符号链接指向的文件,然后将该文件复制到符号链接上:

 cp --remove-destination `readlink bar.pdf` bar.pdf 

当然,如果bar.pdf实际上是一个开始的常规文件,那么这将会破坏文件。 因此,一些理智的检查是明智的。

只是对其他答案进行重新梳理,但增加“完整性检查”以确保链接通过其实是一个象征性的联系:

 removelink() { [ -L "$1" ] && cp --remove-destination "$(readlink "$1")" "$1" } 

这就是说如果文件是一个符号链接,那么运行copy命令。

 for f in $(find -type l);do cp --remove-destination $(readlink $f) $f;done; 
  • 检查当前目录和子目录中的符号链接find -type l
  • 获取链接的文件pathreadlink $f
  • 删除符号链接并复制文件cp --remove-destination $(readlink $f) $f

对于文本文件, sed命令可以在一行中做到这一点,如果你通过就地开关( -i )。 这是因为sed在文件上执行单​​个传递,将输出捕获到临时文件中,随后将其重命名为与原始文件相匹配。

只要做一个没有转换的内联sed

 sed -i ';' /path/to/symbolic/link 

在OSX上

rsync`readlink bar.pdf` bar.pdf

cp上不需要任何有趣的shell脚本, rsync或特殊的GNU选项。 很简单:

 $ mv target link 

如果我们不知道目标,我们用expression式$(readlink link)replace上面的target

 $ mv $(readlink link) link 

mv实用程序对应于POSIX-like系统上的rename系统调用(至less当不在不同的文件系统卷上操作时)。 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 

cp可以删除目标文件:

 cp --remove-destination target filename 

许多人已经提出了以下解决scheme:

 cp --remove-destination `readlink file` file 

但是,这不适用于符号链接的目录。

添加一个recursion强制也不会工作:

 cp -rf --remove-destination `readlink file` file 

cp:不能复制一个目录,path/文件,自己,文件'

因此,首先完全删除符号链接可能更安全:

 resolve-symbolic-link() { if [ -L $1 ]; then temp="$(readlink "$1")"; rm -rf "$1"; cp -rf "$temp" "$1"; fi } 

不完全像你所希望的那样,但把它放到你的shell环境中,它可以是。

这对我来说是完美的( 编辑与空间 ):

 find . -type l | while read f; do /bin/cp -rf --remove-destination -f $(find . -name $(readlink "${f}")) "${f}";done; 

允许您将当前工作文件夹下的所有符号链接recursion转换为常规文件。 也不要求你覆盖。 “/ bin / cp”存在,这样就可以绕过操作系统上可能的cp -i别名,从而防止“-rf”的工作。

没有单一的命令。 但是如果你不想要一个副本,并且你想要的只是另一个参考,你可以用一个链接(又称“硬链接”)replace这个链接。 这只适用于如果他们在同一个分区,顺便说一句。

 rm filename ln target filename 

使用-L标志,Rsync可以主动地使用符号链接。

 [user@workstation ~]$ rsync -h | grep -- -L -L, --copy-links transform symlink into referent file/dir 

这将如下简单: rsync -L <symlink> <destination>

在“相对”符号链接的情况下,你可以添加一个awk语句给@jared答案:

 for f in $(find . -type l); do /bin/cp -rf --remove-destination -f $(find . \! -type l -name $(readlink $f | awk -F"../" '{print $NF}')) $f;done; 

如果有指向目录的符号链接,则需要使用更激进的方法,因为cp –remove-destination与symlinks目录不能一起正常工作

 for f in `find . -type l`; do (cd `dirname $f`; target=`basename $f`; source=`readlink $target` ; rm -rf $target && cp -r $source $target); done 

当然,这可以用更less的开销来编写。 但在我看来,这是相当好的。

此解决scheme适用于符号链接文件和符号链接文件夹/子文件夹。

一行,不需要脚本。 这将是出口或转移其他地方的符号链接的理想select。

把一切都放在一个文件夹中

rsync –archive –copy-links –recursive folderContainingSymlinkFilesAndSymlinkFoldersAndSubfolders myNewFolder

如果你发现自己经常这样做,另一种方法是将kbrock的答案调整到shell脚本中,并将其放入/ usr / bin /中。 有些东西是:

 if [ $# -ne 1 ] then echo "Usage: $0 filename" exit 1 fi [ -L "$1" ] && cp --remove-destination "$(readlink "$1")" "$1" 

chmod + x it,然后把它作为一个普通的命令运行。