如何将符号链接转换为常规文件?
将符号链接转换为常规文件(即符号链接目标的副本)最直接的方法是什么?
假设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
- 获取链接的文件path
readlink $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,然后把它作为一个普通的命令运行。