如何有效地取消引用 `find` *output* 文件名中的所有符号链接?

kam*_*mpu 5 linux find symlink

我需要完全解析并相对于给定目录的路径。这必须有效地完成,因为路径的数量通常大于 100,000。

现状:我有一个包含目录主要符号链接到其他目录,如

foo
 123 -> ../baz/123
 896 -> ../bar/896
Run Code Online (Sandbox Code Playgroud)

(请注意, foo 不仅包含指向目录的符号链接,它还包含我也必须捕获的普通文件。)

这些符号链接目录包含文件。. 我想以表格形式获取这些文件的列表

baz/123/some.file
bar/123/other.file
Run Code Online (Sandbox Code Playgroud)

也就是说,当“查找”找到符号链接时,我希望它在报告内容时取消引用该路径。

所以我从 foo 的父目录运行这个命令:

find -L foo -type f
Run Code Online (Sandbox Code Playgroud)

但这不起作用。

老实说,您希望-L声称“遵循符号链接”的选项能够实现这种行为。但是,其实际的行为是寻找这些目录中的内容,但报告的文件里面他们与他们的非间接引用的名称,即。结果看起来像

foo/baz/123/some.file
foo/bar/896/another.file
Run Code Online (Sandbox Code Playgroud)

结果将用于针对所有 1.完全解析和 2.相对于 foo 的父目录的文件路径列表进行设置操作,因此每个结果也必须满足该标准。为了这些目的,我可以保证所有链接都是可解析的,即。没有一个是圆形的或过深的。大多数(但不是所有)链接都指向目录而不是文件。

目前,我能做的最好的事情就是编写一个 Python 脚本,它将任何未取消引用的路径重写为已解析的路径。但是由于涉及的文件数量在100000+范围内,这不是很实用(而且相当荒谬,因为find已经费心去取消引用它们,它只是没有返回取消引用的路径)。(编辑:请参阅我对这篇文章的评论——我找到了一个非解决方案(因为它有效地完成了工作,但以错误的方式——执行外部命令)。)

我相信我应该可以做这个任务只是find和没有外部命令,但我没有找到该男子页启迪这里-没有的-L-H-P-follow有正确的行为,也没有-printf %l-exec出于显而易见的原因而退出 - 它不是find. 有任何想法吗?

编辑 2:在这一点上,Stephane 已经让我相信 find内部没有特别好的理由,所以我愿意接受任何合理有效的答案。

Sté*_*las 5

你所要求的在一般情况下没有多大意义,所以find没有规定也就不足为奇了。

具有相对目标的符号链接相对于符号链接的路径。因此,举例来说,如果通过遍历由下面的符号链接,目录find的遭遇a/b/c/daa/ba/b/c都是相对或绝对符号链接(或符号链接与符号链接成份的路径),应该怎样做呢?

如果您正在寻找一个find谓词或一个 GNU-printf %指令,它可以扩展为相对于当前目录或任何目录的文件的无符号链接路径,恐怕没有。

如果您使用的是 Linux,则可以使用以下命令获取这些文件的绝对路径:

find -L foo -type f -exec readlink -f {} \;
Run Code Online (Sandbox Code Playgroud)

正如您所发现的,至少存在一个realpath接受多个路径参数的命令,结合标准-exec cmd {} +语法,它会更高效,因为它会根据需要运行尽可能少的 realpath 命令:

find -L foo -type f -exec realpath {} +

find -L foo -type f -print0 | xargs -r0 realpath
Run Code Online (Sandbox Code Playgroud)

可能会更快,好像需要多个realpath命令一样,find可以在第一个realpath开始工作时继续查找更多文件,即使在单处理器系统上也可能使其更有效率。

-print0并且xargs -r0不是标准的,来自 GNU,但可以在许多其他实现中找到,例如大多数现代 BSD。

Zsh 内置了对它的支持:

print -rl foo/***/*(-.:A)
Run Code Online (Sandbox Code Playgroud)

如果您不关心排序顺序,则可以禁用排序并通过以下方式提高效率:

print -rl foo/***/*(-.oN:A)
Run Code Online (Sandbox Code Playgroud)

如果您想将这些转换为当前目录的相对路径,您可以查看那个 SO question

如果您知道所有这些文件在当前目录中都有一个绝对规范路径(其组件都不是符号链接),您可以将其简化为(仍然带有zsh):

files=(foo/***/*(-.:A))
print -rl -- ${files#$PWD/}
Run Code Online (Sandbox Code Playgroud)

虽然简短方便,并且可以处理任何字符文件名包含的内容,但我怀疑它会比find+快realpath

使用 Debianrealpath和 GNU 工具,您可以:

cd -P .
find -L foo -type f -exec realpath -z {} + | 
  gawk -v p="$PWD" -v l="${#PWD}" -v RS='\0' -vORS='\0' '
    substr($0, 1, l+1) == p "/" {$0 = substr($0, l+2)}; 1' |
  xargs -r0 whatever you want to do with them
Run Code Online (Sandbox Code Playgroud)

正如我现在意识到的,现在有一个realpath最新版本的 GNU coreutils,它具有您正在寻找的确切功能,所以这只是一个问题

find -L foo -type f -print0 |
  xargs -r0 realpath -z --relative-base . |
  xargs -r0 whatever you want to do with them
Run Code Online (Sandbox Code Playgroud)

(即使对于符号链接自由路径不在当前工作目录下的文件--relative-to .--relative-base .如果您想要相对路径,请使用代替)。