我想递归地查找*.pdf
目录中的每个文件,~/foo
其基本名称与文件的父目录的名称匹配。
例如,假设目录结构~/foo
如下所示
foo
├── dir1
│ ├── dir1.pdf
│ └── dir1.txt
├── dir2
│ ├── dir2.tex
│ └── spam
│ └── spam.pdf
└── dir3
├── dir3.pdf
└── eggs
└── eggs.pdf
运行我想要的命令将返回
~/foo/dir1/dir1.pdf
~/foo/dir2/spam/spam.pdf
~/foo/dir3/dir3.pdf
~/foo/dir3/eggs/eggs.pdf
这可能使用find
或其他一些核心实用程序吗?我认为使用-regex
to 选项可以做到这一点,find
但我不确定如何编写正确的模式。
使用 GNU
find
:-regextype egrep
使用 egrep 风格的正则表达式。.*/
匹配祖父母方向。([^/]+)/
匹配组中的父目录。\1\.pdf
用于backreference
匹配文件名作为父目录。更新
一个人(我自己)可能认为这
.*
已经够贪心了,没有必要/
从父匹配中排除:上面的命令不能很好地工作,因为它会计算
./a/b/a/b.pdf
:.*/
火柴./
(.+)/
火柴a/b/
\1.pdf
火柴a/b.pdf
find .. -exec sh -c ''
使用 shell 构造来匹配基本名称和上面的直接路径的传统循环变体将在下面执行。分解各个参数扩展
file
包含从命令.pdf
返回的文件的完整路径find
"${file##*/}"
仅包含最后一部分之后的部分,/
即仅包含文件的基本名称"${file%/*}"
包含到最后的路径,/
即结果的基本名称部分除外"${path##*/}"
包含变量中最后一个之后的部分/
,path
即文件基本名称上方的直接文件夹路径"${base%.*}"
包含.pdf
删除扩展名的基本名称部分因此,如果没有扩展名的基本名称与上面的直接文件夹的名称匹配,我们将打印路径。
与Inian 的答案相反,即查找目录,然后查看它们是否包含具有特定名称的文件。
以下打印找到的文件相对于目录的路径名
foo
:${dirpath##*/}
将替换为目录路径的文件名部分,并且可以替换为$(basename "$dirpath")
.对于喜欢短路语法的人:
这样做的好处是您可能拥有比目录更多的 PDF 文件。如果将查询限制为较小的数量(目录数量),则涉及的测试数量会减少。
例如,如果一个目录包含 100 个 PDF 文件,这只会尝试检测其中一个,而不是根据目录的名称测试所有 100 个文件的名称。
与
zsh
:请注意,虽然
**/
不会遵循符号链接,但*/
会。没有指定,但如果有人感兴趣,这里是一个没有正则表达式的解决方案。
我们可以使用
find . -type f
来获取文件,然后使用dirname
和basename
编写条件。实用程序具有以下行为:basename
只返回最后一个文件名/
:dirname
给出到 final 的整个路径/
:因此,
basename $(dirname $file)
给出文件的父目录。解决方案
结合以上内容形成条件,然后仅在条件返回 true 时
"$(basename $file)" = "$(basename $(dirname $file))".pdf
打印每个结果。find
在上面的示例中,我们添加了一个名称中带有空格的目录/文件来处理这种情况(感谢评论中的@Kusalananda)
我每天都在Find程序上进行bash globbing,简单的循环字符串测试。称我为非理性,虽然它可能不是最理想的,但这样简单的代码对我来说是诀窍:可读和可重用,甚至令人满意!因此,请允许我建议以下组合:
• bash globstar :
for f in ** ; do ...
**循环遍历当前目录和所有子文件夹中的每个文件.. 以检查当前会话中的 globstar 状态:shopt -p globstar
. 激活 globstar:shopt -s globstar
。• “文件”实用程序:
if [[ $(file "$f") =~ pdf ]]; then ...
检查pdf的实际文件格式- 比仅测试文件扩展名更强大• basename, dirname:将文件名与紧接其上的目录名进行比较。
basename
返回文件名 -dirname
返回整个目录路径 - 结合这两个函数只返回一个包含匹配文件的目录。我将每一个都放在一个变量(_mydir和_myf)中,然后使用=~进行简单的测试以进行字符串匹配。一个微妙之处:删除文件名中的任何“点”以避免将文件名与快捷方式也是“。”的当前目录匹配。- 我在变量_myf上使用了直接字符串替换:
${_myf//./}
- 不是很优雅,但它有效。正匹配将返回每个文件的路径 - 连同当前文件夹的完整路径,在输出前加上 :$(pwd)/
。代码