我想递归查找*.pdf
目录中~/foo
基本名称与文件父目录名称匹配的每个文件。
例如,假设目录结构~/foo
如下所示
foo
??? dir1
? ??? dir1.pdf
? ??? dir1.txt
??? dir2
? ??? dir2.tex
? ??? spam
? ??? spam.pdf
??? dir3
??? dir3.pdf
??? eggs
??? eggs.pdf
Run Code Online (Sandbox Code Playgroud)
运行我想要的命令会返回
~/foo/dir1/dir1.pdf
~/foo/dir2/spam/spam.pdf
~/foo/dir3/dir3.pdf
~/foo/dir3/eggs/eggs.pdf
Run Code Online (Sandbox Code Playgroud)
这是否可以使用find
或其他一些核心实用程序?我认为使用-regex
选项是可行的,find
但我不确定如何编写正确的模式。
ded*_*sdi 16
使用 GNU find
:
find . -regextype egrep -regex '.*/([^/]+)/\1\.pdf'
Run Code Online (Sandbox Code Playgroud)
-regextype egrep
使用 egrep 风格的正则表达式。.*/
匹配祖父指令。([^/]+)/
匹配组中的父目录。\1\.pdf
用于backreference
将文件名匹配为父目录。更新
一个人(我自己)可能认为这.*
已经足够贪婪了,没有必要/
从父匹配中排除:
find . -regextype egrep -regex '.*/(.+)/\1\.pdf'
Run Code Online (Sandbox Code Playgroud)
上面的命令不能很好地工作,因为它 mathches ./a/b/a/b.pdf
:
.*/
火柴 ./
(.+)/
火柴 a/b/
\1.pdf
火柴 a/b.pdf
find .. -exec sh -c ''
使用 shell 构造匹配基本名称和上面的直接路径的传统循环变体将在下面执行。
find foo/ -name '*.pdf' -exec sh -c '
for file; do
base="${file##*/}"
path="${file%/*}"
if [ "${path##*/}" = "${base%.*}" ]; then
printf "%s\n" "$file"
fi
done' sh {} +
Run Code Online (Sandbox Code Playgroud)
分解单个参数扩展
file
包含.pdf
从find
命令返回的文件的完整路径"${file##*/}"
只包含最后一个之后的部分,/
即只包含文件的基本名称"${file%/*}"
包含到最终/
ie的路径,结果的 basename 部分除外"${path##*/}"
包含在最后部分/
从path
变量,即:该文件的基本名称上面立即文件夹路径"${base%.*}"
包含.pdf
删除扩展名的基本名称部分因此,如果没有扩展名的基本名称与上面直接文件夹的名称匹配,我们将打印路径。
Inian 的答案相反,即查找目录,然后查看它们是否包含具有特定名称的文件。
以下打印找到的文件相对于目录的路径名foo
:
find foo -type d -exec sh -c '
for dirpath do
pathname="$dirpath/${dirpath##*/}.pdf"
if [ -f "$pathname" ]; then
printf "%s\n" "$pathname"
fi
done' sh {} +
Run Code Online (Sandbox Code Playgroud)
${dirpath##*/}
将被目录路径的文件名部分替换,并且可以被替换为$(basename "$dirpath")
.
对于喜欢短路语法的人:
find foo -type d -exec sh -c '
for dirpath do
pathname="$dirpath/${dirpath##*/}.pdf"
[ -f "$pathname" ] && printf "%s\n" "$pathname"
done' sh {} +
Run Code Online (Sandbox Code Playgroud)
这样做的好处是您可能拥有比目录更多的 PDF 文件。如果通过较小的数量(目录数量)限制查询,则涉及的测试数量会减少。
例如,如果单个目录包含 100 个 PDF 文件,这将仅尝试检测其中一个,而不是根据目录的名称测试所有 100 个文件的名称。