AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 520078
Accepted
Brian Fitzpatrick
Brian Fitzpatrick
Asked: 2019-05-21 20:49:01 +0800 CST2019-05-21 20:49:01 +0800 CST 2019-05-21 20:49:01 +0800 CST

查找具有给定扩展名的所有文件,其基本名称是父目录的名称

  • 772

我想递归地查找*.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或其他一些核心实用程序吗?我认为使用-regexto 选项可以做到这一点,find但我不确定如何编写正确的模式。

find
  • 6 6 个回答
  • 1344 Views

6 个回答

  • Voted
  1. Best Answer
    dedowsdi
    2019-05-21T21:16:53+08:002019-05-21T21:16:53+08:00

    使用 GNU find:

    find . -regextype egrep -regex '.*/([^/]+)/\1\.pdf'
    
    • -regextype egrep使用 egrep 风格的正则表达式。
    • .*/匹配祖父母方向。
    • ([^/]+)/匹配组中的父目录。
    • \1\.pdf用于backreference匹配文件名作为父目录。

    更新

    一个人(我自己)可能认为这.*已经够贪心了,没有必要/从父匹配中排除:

    find . -regextype egrep -regex '.*/(.+)/\1\.pdf'
    

    上面的命令不能很好地工作,因为它会计算./a/b/a/b.pdf:

    • .*/火柴./
    • (.+)/火柴a/b/
    • \1.pdf火柴a/b.pdf
    • 16
  2. Inian
    2019-05-21T21:49:26+08:002019-05-21T21:49:26+08:00

    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 {} +
    

    分解各个参数扩展

    • file包含从命令.pdf返回的文件的完整路径find
    • "${file##*/}"仅包含最后一部分之后的部分,/即仅包含文件的基本名称
    • "${file%/*}"包含到最后的路径,/即结果的基本名称部分除外
    • "${path##*/}"包含变量中最后一个之后的部分/,path即文件基本名称上方的直接文件夹路径
    • "${base%.*}"包含.pdf删除扩展名的基本名称部分

    因此,如果没有扩展名的基本名称与上面的直接文件夹的名称匹配,我们将打印路径。

    • 7
  3. Kusalananda
    2019-05-21T22:58:58+08:002019-05-21T22:58:58+08:00

    与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 {} +
    

    ${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 {} +
    

    这样做的好处是您可能拥有比目录更多的 PDF 文件。如果将查询限制为较小的数量(目录数量),则涉及的测试数量会减少。

    例如,如果一个目录包含 100 个 PDF 文件,这只会尝试检测其中一个,而不是根据目录的名称测试所有 100 个文件的名称。

    • 7
  4. Stéphane Chazelas
    2019-05-21T21:59:01+08:002019-05-21T21:59:01+08:00

    与zsh:

    printf '%s\n' **/*/*.pdf(e@'[[ $REPLY:t = $REPLY:h:t.pdf ]]'@)
    

    请注意,虽然**/不会遵循符号链接,但*/会。

    • 3
  5. user1717828
    2019-05-22T07:58:13+08:002019-05-22T07:58:13+08:00

    没有指定,但如果有人感兴趣,这里是一个没有正则表达式的解决方案。

    我们可以使用find . -type f来获取文件,然后使用dirname和basename编写条件。实用程序具有以下行为:

    $ find . -type f
    ./dir2/spam/spam.pdf
    ./dir2/dir2.tex
    ./dir3/dir3.pdf
    ./dir3/eggs/eggs.pdf
    ./dir1/dir1.pdf
    ./dir1/dir1.txt
    

    basename只返回最后一个文件名/:

    $ for file in $(find . -type f); do basename $file; done
    spam.pdf
    dir2.tex
    dir3.pdf
    eggs.pdf
    dir1.pdf
    dir1.txt
    

    dirname给出到 final 的整个路径/:

    $ for file in $(find . -type f); do dirname $file; done
    ./dir2/spam
    ./dir2
    ./dir3
    ./dir3/eggs
    ./dir1
    ./dir1
    

    因此,basename $(dirname $file)给出文件的父目录。

    $ for file in $(find . -type f); do basename $(dirname $file) ; done
    spam
    dir2
    dir3
    eggs
    dir1
    dir1
    

    解决方案

    结合以上内容形成条件,然后仅在条件返回 true 时"$(basename $file)" = "$(basename $(dirname $file))".pdf打印每个结果。find

    $ while read file; do if [ "$(basename "$file")" = "$(basename "$(dirname "$file")")".pdf ]; then echo $file; fi done < <(find . -type f)
    ./dir2/spam/spam.pdf
    ./dir3/dir3.pdf
    ./dir3/eggs/eggs.pdf
    ./dir1/dir1.pdf
    ./Final Thesis/grits/grits.pdf
    ./Final Thesis/Final Thesis.pdf
    

    在上面的示例中,我们添加了一个名称中带有空格的目录/文件来处理这种情况(感谢评论中的@Kusalananda)

    • 2
  6. docgyneco69
    2019-05-24T18:34:06+08:002019-05-24T18:34:06+08:00

    我每天都在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)/。

    代码

    for f in ** ; do
      if [[ $(file "$f") =~ PDF ]]; then
        _mydir="$(basename $(dirname $f))" ; 
        _myf="$(basename $f)" ; 
        [[ "${_myf//./}" =~ "$_mydir" ]] && echo -e "$(pwd)/$f" ; 
      fi ; 
    done
    
    • 0

相关问题

  • 如果未引用 -name 后面的模式,则 find 的奇怪行为

  • 将变量从子shell打印到父shell [重复]

  • 检查某个文件夹是否存在于某个目录中

  • 从命令行查找和替换 CSS 文件中的颜色

  • GNU find:在-exec中获取绝对和相对路径

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    模块 i915 可能缺少固件 /lib/firmware/i915/*

    • 3 个回答
  • Marko Smith

    无法获取 jessie backports 存储库

    • 4 个回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    user12345 无法获取 jessie backports 存储库 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl 为什么大多数 systemd 示例都包含 WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve