bash 用原始符号替换所有符号链接
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7167424/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
replace all symlinks with original
提问by Ken Hirakawa
I have the following directory structure
我有以下目录结构
/symdir
sym1 -> ../dir1
sym2 -> ../dir2
hello.txt
And then
进而
/dir1
some
files
here
/dir2
more
files
I would like to replace the symlinks in symdir (sym1, sym2) with the originals. I.e.
我想用原件替换 symdir (sym1, sym2) 中的符号链接。IE
some_awesome_bash_func symdir symdir_output
Would create
会创造
/symdir_output
/dir1
some
files
here
/dir2
more
files
hello.txt
How would I accomplish this?
我将如何做到这一点?
采纳答案by Daniel Haley
Probably not the best way, but it works:
可能不是最好的方法,但它有效:
#!/usr/bin/bash
for link in $(find /symdir -type l)
do
loc="$(dirname "$link")"
dir="$(readlink "$link")"
mv "$dir" "$loc"
rm "$link"
done
回答by Julien Palard
My very personal trick for files (not directories):
我非常个人的文件(不是目录)技巧:
sed -i '' **/*
Note that I'm using **which uses the bash globstar option, you may have to enable it beforehand:
请注意,我正在使用**which 使用 bash globstar 选项,您可能必须事先启用它:
shopt -s globstar
How it works
这个怎么运作
I trick sedto do the job, by using an implementation detail of the sedinplace mode.
我sed通过使用sed就地模式的实现细节来完成这项工作。
sedis a tool to edit streams of text. The -ioption of sedmeans inplace, the empty string ''is the instruction set: so there's no instruction, sedwill do nothing. **/*is a bash globstarpattern meaning "all files and all folders, at all depth, from here".
sed是一种编辑文本流的工具。该-i的选择sed手段inplace,空字符串''是指令集:所以没有指令,sed不会做任何事情。**/*是一个 bashglobstar模式,意思是“所有文件和所有文件夹,所有深度,从这里”。
The algorithm seduses to edit a file inplace is:
sed用于就地编辑文件的算法是:
- Create a temporary file as the output file,
- for each line in the input file:
- apply the transformation, write to the output file.
- Move the output file over the input file.
- 创建一个临时文件作为输出文件,
- 对于输入文件中的每一行:
- 应用转换,写入输出文件。
- 将输出文件移到输入文件上。
As I'm asking no transformations (the empty string), the algorithm can be simplified as:
由于我不要求转换(空字符串),因此算法可以简化为:
- Create a temporary file,
- copy the content of the original file to the temporary file
- move the temporary file over the original file.
- 创建一个临时文件,
- 将原文件的内容复制到临时文件中
- 将临时文件移动到原始文件上。
The temporary file is a real file, sedcompletly ignores that the input file was a symlink, it just reads it. So at the last step, when sedmoves the temporary file over the real file, it "overwrite" the symlink with a real file, that's what we wanted.
临时文件是一个真实的文件,sed完全忽略输入文件是一个符号链接,它只是读取它。所以在最后一步,当sed将临时文件移动到真实文件上时,它会用真实文件“覆盖”符号链接,这就是我们想要的。
This also explains why it won't work to transform a "symlink to a directory" to a real directory: sedworks on file contents.
这也解释了为什么将“符号链接到目录”转换为真实目录不起作用:sed适用于文件内容。
回答by Jonathan Amend
You can do this easily with rsync:
您可以使用 rsync 轻松完成此操作:
rsync symdir/ symdir_output/ -a --copy-links -v
(-a means preserve basically every detail about the files, --copy-links overrides -a to turn symlinks into the real files/directories, and -v is for verbose)
(-a 表示基本上保留有关文件的所有细节,--copy-links 覆盖 -a 将符号链接转换为真实的文件/目录,-v 表示详细)
Edit:
编辑:
Sorry, my solution doesn't do exactly what you asked for. It will preserve the symlink's names instead of using the destination names. symdir_output would have sym1 and sym2 instead of dir1 and dir2 (though sym1 and sym2 would be a real copy of dir1 and dir2). Hope it still works for you.
抱歉,我的解决方案不能完全满足您的要求。它将保留符号链接的名称而不是使用目标名称。symdir_output 将有 sym1 和 sym2 而不是 dir1 和 dir2(尽管 sym1 和 sym2 将是 dir1 和 dir2 的真实副本)。希望它仍然对你有用。
回答by PinkFloyd
a related answer, this solution keeps the file at it's original place and creates a copy in place of the symlink
一个相关的答案,此解决方案将文件保留在其原始位置并创建一个副本来代替符号链接
#/bin/bash
for f in $(find . -maxdepth 1 -type l)
do
cp --remove-destination $(readlink -e $f) $f
done
回答by Mike Gebirge
tl;dr: much more general, much more reliable answer:
tl; dr:更一般,更可靠的答案:
find -type l -exec sh -c 'PREV=$(realpath -- "") && rm -- "" && cp -ar -- "$PREV" ""' resolver {} \;
The "rsync-to-other-destination" approach is strictly superior, and usually leads to better design.
“rsync-to-other-destination”方法绝对优越,通常会带来更好的设计。
The answer by @PinkFloyd doesn't quite work with unusual filenames, "buried" symlinks, or symlinked directories. I came here because I wanted to resolve directory-symlinks, so I expect others to find this question for this reason, too. Also, my version of cp(GNU coreutils 8.25) doesn't properly handle --remove-destinationfor @PinkFloyd's answer to work with directories. So this answer uses manual rm.
@PinkFloyd 的答案不适用于不寻常的文件名、“埋藏”符号链接或符号链接目录。我来到这里是因为我想解决目录符号链接,所以我希望其他人也因为这个原因找到这个问题。此外,我的cp(GNU coreutils 8.25)版本无法正确处理--remove-destination@PinkFloyd 使用目录的答案。所以这个答案使用 manual rm。
Also note:
另请注意:
- absence of
-rf. That's because a symlink is not a directory, and should not need-r. And unless you have symlinks with restricted permissions (why would you ever want that?!), you don't need-feither. realpathis perfectly fine in this context, because it allows us to find out the actual location in the current system, in the current context, and nothing else matters. This path won't be written to disk, so this is not an error.- the
resolverstring is forsh. Seeman sh. - the double-dashes everywhere are necessary, in case some symlink is called
--versionor similar. - due to the preorder-guarantee of
find("parent directory is listed at somewhere before its content"), this would first replace the parent directory, and then any symlinks within the symlinked directory. So it would work perfectly fine with "stacked" symlinks.
- 的缺席
-rf。那是因为符号链接不是目录,因此不需要-r. 除非你有权限受限的符号链接(你为什么想要那个?!),你也不需要-f。 realpath在这种情况下完全没问题,因为它允许我们找出当前系统中的实际位置,在当前上下文中,其他无关紧要。此路径不会写入磁盘,因此这不是错误。- 该
resolver字符串用于sh. 见man sh。 - 任何地方的双破折号都是必要的,以防某些符号链接被调用
--version或类似。 - 由于
find(“父目录在其内容之前的某处列出”)的预购保证,这将首先替换父目录,然后是符号链接目录中的任何符号链接。因此,它可以与“堆叠”符号链接完美配合。
回答by Orion Edwards
I did it this way:
我是这样做的:
ls -la | awk '/-\>/{system("rm "); system("cp "" .")}'
How it works:
这个怎么运作:
ls -laoutputs something like this:
ls -la输出是这样的:
lrwxr-xr-x 1 username groupname 44 10 Oct 12:17 Queue.swift -> ../../../Platform/DataStructures/Queue.swift
Column 10 is Queue.swiftwhich is the name of the local file.
Column 12 is ../../../Platform/DataStructures/Queue.swiftwhich is the name of the link target
第 10 列是Queue.swift本地文件的名称。
第 12 列是../../../Platform/DataStructures/Queue.swift链接目标的名称
The first part of the awkcommand is '/-\>/'which means "match lines which contain ->using a regex
该awk命令的第一部分'/-\>/'表示“匹配包含->使用正则表达式的行
The next part of the awk command is two calls to system
awk 命令的下一部分是两次调用 system
First system("rm "$10)which expands to system("rm Queue.swift").
This will cause the original file (the symlink) to get deleted
首先system("rm "$10)扩展到system("rm Queue.swift").
这将导致原始文件(符号链接)被删除
Second is system("cp "$12" .")which expands to system("cp ../../../Platform/DataStructures/Queue.swift .")
其次是system("cp "$12" .")扩展到system("cp ../../../Platform/DataStructures/Queue.swift .")
Putting it all together, what happens is for each file (which is a symlink), first we delete the symlink, then we copy the target file in it's place.
总而言之,对于每个文件(这是一个符号链接),首先我们删除符号链接,然后将目标文件复制到它的位置。
Although it's not part of the original question, I was using this in conjunction with git. If you happen to be doing that too, you can run git status .afterwards and you should see a bunch of type changes (and nothing else), like this:
虽然它不是原始问题的一部分,但我将它与 git 结合使用。如果你碰巧也这样做,你可以在git status .之后运行,你应该看到一堆类型的变化(没有别的),像这样:
typechange: Queue.swift
回答by FGiorlando
here is a slightly more general solution, based on @Daniel Haley
这是一个更通用的解决方案,基于@Daniel Haley
it also preserves the symlinks for reference and asks the user to select a directory to edit.
它还保留符号链接以供参考,并要求用户选择要编辑的目录。
ls
read -p 'Which directory do you want to update?: ' linkdir
pushd $linkdir
for linkname in $(find ./ -type l)
do
orig=$(readlink $linkname)
mv $linkname ${linkname}.linkbak
cp $orig $linkname
done
popd

