仅查找包含与文件夹同名的文件的文件夹

Por*_*ine 8 directory find gnu filenames

我想找到所有子文件夹,其中包含具有相同名称(和扩展名.md)的降价文件。

例如:我想查找以下子文件夹:

Apple/Banana/Orange      #Apple/Banana/Orange/Orange.md exists
Apple/Banana             #Apple/Banana/Banana.md exists
Apple/Banana/Papaya      #Apple/Banana/Papaya/Papaya.md exists
Run Code Online (Sandbox Code Playgroud)
  • 注意:目录中可以有其他文件或子目录。

有什么建议?


可以使用以下代码测试问题的解决方案:

#!/usr/bin/env bash
# - goal: "Test"
# - author: Nikhil Agarwal
# - date: Wednesday, August 07, 2019
# - status: P T' (P: Prototyping, T: Tested)
# - usage: ./Test.sh
# - include:
#   1.
# - refer:
#   1. [directory - Find only those folders that contain a File with the same name as the Folder - Unix & Linux Stack Exchange](https://unix.stackexchange.com/questions/534190/find-only-those-folders-that-contain-a-file-with-the-same-name-as-the-folder)
# - formatting:
#   shellcheck disable=
#clear

main() {
    TestData
    ExpectedOutput
    TestFunction "${1:?"Please enter a test number, as the first argument, to be executed!"}"
}

TestFunction() {
    echo "Test Function"
    echo "============="
    "Test${1}"
    echo ""
}

Test1() {
    echo "Description: Thor"
    find . -type f -regextype egrep -regex '.*/([^/]+)/\1\.md$' | sort
    echo "Observation: ${Green:=}Pass, but shows filepath instead of directory path${Normal:=}"
}

Test2() {
    echo "Description: Kusalananda1"
    find . -type d -exec sh -c '
    dirpath=$1
    set -- "$dirpath"/*.md
    [ -f "$dirpath/${dirpath##*/}.md" ] && [ "$#" -eq 1 ]' sh {} \; -print | sort
    echo "Observation: ${Red:=}Fails as it ignores B.md${Normal:=}"
}

Test3() {
    echo "Description: Kusalananda2"
    find . -type d -exec sh -c '
    for dirpath do
        set -- "$dirpath"/*.md
        if [ -f "$dirpath/${dirpath##*/}.md" ] && [ "$#" -eq 1 ]
        then
            printf "%s\n" "$dirpath"
        fi
    done' sh {} + | sort
    echo "Observation: ${Red:=}Fails as it ignores B.md${Normal:=}"
}

Test4() {
    echo "Description: steeldriver1"
    find . -type d -exec sh -c '[ -f "$1/${1##*/}.md" ]' find-sh {} \; -print | sort
    echo "Observation: ${Green:=}Pass${Normal:=}"
}

Test5() {
    echo "Description: steeldriver2"
    find . -type d -exec sh -c '
  for d do
    [ -f "$d/${d##*/}.md" ] && printf "%s\n" "$d"
  done' find-sh {} + | sort
    echo "Observation: ${Green:=}Pass${Normal:=}"
}

Test6() {
    echo "Description: Stéphane Chazelas"
    find . -name '*.md' -print0 \
        | gawk -v RS='\0' -F/ -v OFS=/ '
    {filename = $NF; NF--
     if ($(NF)".md" == filename) include[$0]
     else exclude[$0]
    }
    END {for (i in include) if (!(i in exclude)) print i}'
    echo "Observation: ${Red:=}Fails as it ignores B.md${Normal:=}"
}

Test7() {
    echo "Description: Zach"
    #shellcheck disable=2044
    for fd in $(find . -type d); do
        dir=${fd##*/}
        if [ -f "${fd}/${dir}.md" ]; then
            ls "${fd}/${dir}.md"
        fi
    done
    echo "Observation: ${Green:=}Pass but shows filepath instead of directory${Normal:=}"
}
ExpectedOutput() {
    echo "Expected Output"
    echo "==============="
    cat << EOT
./GeneratedTest/A
./GeneratedTest/A/AA
./GeneratedTest/B
./GeneratedTest/C/CC1
./GeneratedTest/C/CC2
EOT
}

TestData() {
    rm -rf GeneratedTest

    mkdir -p GeneratedTest/A/AA
    touch GeneratedTest/index.md
    touch GeneratedTest/A/A.md
    touch GeneratedTest/A/AA/AA.md

    mkdir -p GeneratedTest/B
    touch GeneratedTest/B/B.md
    touch GeneratedTest/B/index.md

    mkdir -p GeneratedTest/C/CC1
    touch GeneratedTest/C/index.md
    touch GeneratedTest/C/CC1/CC1.md

    mkdir -p GeneratedTest/C/CC2
    touch GeneratedTest/C/CC2/CC2.md

    mkdir -p GeneratedTest/C/CC3
    touch GeneratedTest/C/CC3/CC.md

    mkdir -p GeneratedTest/C/CC4
}
main "$@"
Run Code Online (Sandbox Code Playgroud)

Tho*_*hor 13

假设您的文件命名合理,即不需要-print0等。您可以使用 GNU find 执行此操作,如下所示:

find . -type f -regextype egrep -regex '.*/([^/]+)/\1\.md$'
Run Code Online (Sandbox Code Playgroud)

输出:

find . -type f -regextype egrep -regex '.*/([^/]+)/\1\.md$'
Run Code Online (Sandbox Code Playgroud)

如果您只想要目录名称,请添加一个-printf参数:

find . -type f -regextype egrep -regex '.*/([^/]+)/\1\.md$' -printf '%h\n'
Run Code Online (Sandbox Code Playgroud)

在更新的测试数据上运行时的输出:

./Apple/Banana/Orange/Orange.md
./Apple/Banana/Papaya/Papaya.md
./Apple/Banana/Banana.md
Run Code Online (Sandbox Code Playgroud)

  • @吉姆。除了将它管道到面向行的工具会在文件名中的某些字符上中断,例如换行符。 (3认同)

Kus*_*nda 6

find . -type d -exec sh -c '
    dirpath=$1
    set -- "$dirpath"/*.md
    [ -f "$dirpath/${dirpath##*/}.md" ] && [ "$#" -eq 1 ]' sh {} \; -print
Run Code Online (Sandbox Code Playgroud)

以上将查找当前目录(包括当前目录)下的所有目录,并为每个目录执行一个简短的 shell 脚本。

shell 代码将测试是否存在与目录中的目录同名的 markdown 文件,以及这是否是该*.md目录中的唯一名称。如果这样的文件存在并且它是唯一的*.md名称,则内联 shell 脚本将以零退出状态退出。否则,它会以非零退出状态(信令失败)退出。

set -- "$dirpath"/*.md位将位置参数设置为匹配模式的路径名列表(匹配.md目录中带有后缀的任何名称)。然后我们可以稍后使用它$#来查看我们从中获得了多少匹配。

如果 shell 脚本成功退出,-print将打印到找到的目录的路径。

使用更少的内联脚本调用的稍微快一点的版本,但这并不能让您对找到的路径名find本身做更多的事情(尽管内联脚本可能会进一步扩展):

find . -type d -exec sh -c '
    for dirpath do
        set -- "$dirpath"/*.md
        [ -f "$dirpath/${dirpath##*/}.md" ] &&
        [ "$#" -eq 1 ] &&
        printf "%s\n" "$dirpath"
    done' sh {} +
Run Code Online (Sandbox Code Playgroud)

相同的命令,但不关心.md目录中是否还有其他文件:

find . -type d -exec sh -c '
    dirpath=$1
    [ -f "$dirpath/${dirpath##*/}.md" ]' sh {} \; -print
Run Code Online (Sandbox Code Playgroud)
find . -type d -exec sh -c '
    for dirpath do
        [ -f "$dirpath/${dirpath##*/}.md" ] &&
        printf "%s\n" "$dirpath"
    done' sh {} +
Run Code Online (Sandbox Code Playgroud)

也可以看看:


Sté*_*las 6

在 GNU 系统上,您可以执行以下操作:

find . -name '*.md' -print0 |
  gawk -v RS='\0' -F/ -v OFS=/ '
    {filename = $NF; NF--
     if ($(NF)".md" == filename) include[$0]
     else exclude[$0]
    }
    END {for (i in include) if (!(i in exclude)) print i}'
Run Code Online (Sandbox Code Playgroud)

  • 您介意重新包含您提出的 zsh 解决方案作为替代方案吗?这对我们这些试图了解更多 zsh 的人会有所帮助 (3认同)