Chỉ tìm những thư mục chứa Tệp có cùng tên với Thư mục


8

Tôi muốn tìm tất cả các thư mục con chứa tệp đánh dấu có cùng tên (và phần mở rộng .md).

Ví dụ: Tôi muốn Tìm các thư mục con sau:

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
  • Lưu ý: Có thể có các tệp hoặc thư mục con khác trong thư mục.

Bất kỳ đề xuất?


Các giải pháp cho vấn đề có thể được kiểm tra bằng mã sau đây:

#!/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](/unix/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 "$@"

1
Về nhận xét cuối cùng của bạn. Lưu ý rằng một số câu trả lời làm những điều khác nhau từ những người khác. Ví dụ của Mine và Stéphane, đã giải thích "Ghi chú" đầu tiên của bạn là "nếu có các tệp đánh dấu khác trong thư mục nào , đừng trả lại thư mục đó" trong khi những người khác không thấy (theo như tôi có thể thấy). Ngoài ra, chỉ có bạn có thể chọn câu trả lời hữu ích nhất cho bạn . Câu trả lời ở đây sẽ tiếp tục nhận được phiếu bầu lên và xuống sau khi bạn đã chấp nhận câu trả lời, tùy thuộc vào những gì người đọc khác thấy hữu ích nhất.
Kusalananda

Khi bạn nói "Không nên tìm thấy các thư mục chứa tệp đánh dấu có tên khác nhau", bạn có nghĩa là loại trừ các thư mục có cả hai? Ví dụ, nếu bạn có foo/foo.mdfoo/bar.mdnên foođược bao gồm hoặc loại trừ?
Kevin

@Kevin Trong ví dụ mà bạn đưa ra, tôi có ý bao gồm foo. Nhưng thật không may, nhiều người giải thích theo cách khác và họ biện minh rằng. Vì vậy, tôi nghĩ rằng tôi không rõ ràng trong giao tiếp. Vì vậy, tôi chấp nhận câu trả lời không bao gồm foo.
Nikhil

Nếu bạn sử dụng -printfvới find, bạn có thể nhận được bất kỳ phần nào của trận đấu mà bạn muốn, xem phần chỉnh sửa của tôi
Thor

Câu trả lời:


13

Giả sử các tệp của bạn được đặt tên hợp lý, tức là không cần -print0v.v. Bạn có thể làm điều này với GNU find như thế này:

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

Đầu ra:

./Apple/Banana/Orange/Orange.md
./Apple/Banana/Papaya/Papaya.md
./Apple/Banana/Banana.md

Nếu bạn chỉ muốn tên thư mục, hãy thêm một -printfđối số:

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

Đầu ra khi chạy trên dữ liệu thử nghiệm được cập nhật của bạn:

GeneratedTest/A/AA
GeneratedTest/A
GeneratedTest/C/CC2
GeneratedTest/C/CC1
GeneratedTest/B

Ngay cả khi không có GNU, hãy tìm:find . -type f | egrep '.*/([^/]+)/\1\.md$'
Jim L.

3
@JimL. Ngoại trừ việc chuyển nó sang một công cụ định hướng dòng sẽ phá vỡ một số ký tự trong tên tệp, như dòng mới.
Kusalananda

1
@Kusalananda Đồng ý, tuy nhiên, câu trả lời cụ thể này được dựa trên các tệp "được đặt tên hợp lý" không yêu cầu print0.
Jim L.

@Thor %htrong printf được sử dụng cho loại dữ liệu int được định dạng. Tham khảo: chuỗi định dạng printf - Wikipedia . Bạn có thể vui lòng giải thích phần đó? Làm thế nào đang %hđược sử dụng ở đây?
Nikhil

@Nikhil: Không phải với find, xem phần 3.2.2.1 trong hướng dẫn để biết thêm chi tiết.
Thor

6

Trên hệ thống GNU, bạn có thể làm một cái gì đó như:

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}'

3
bạn có phiền khi bao gồm giải pháp zsh được đề xuất của bạn như là một thay thế? nó sẽ hữu ích cho những người trong chúng ta đang cố gắng tìm hiểu thêm về zsh
Steeldo

Cho rằng câu trả lời này đã nhận được nhiều phiếu bầu hơn: Đối với những người đang nâng cao câu trả lời này, bạn có thể vui lòng xác định lý do tại sao câu trả lời này tốt hơn phần còn lại không? Nó sẽ giúp tôi chọn câu trả lời phù hợp nhất.
Nikhil

Stéphane, tôi đồng ý với thép. Hãy đề cập đến zshgiải pháp trước đó (tôi tin rằng, hai trong số các upvote), và thoải mái chỉ ra bất kỳ sai sót nào trong đó có thể khiến bạn phải gỡ bỏ nó.
Kusalananda

1
@steel ấn, trong cách tiếp cận zsh đó, tôi (giống như bạn) đã bỏ lỡ một phần yêu cầu rằng các thư mục chứa các tệp md khác phải được bỏ qua.
Stéphane Chazelas

@ StéphaneChazelas OP chỉ làm rõ trong các ý kiến mà anh ta thực sự muốn nói đến, đó chỉ là những cụm từ kém và mọi người đã hiểu nó theo nghĩa đen.
Kevin

6
find . -type d -exec sh -c '
    dirpath=$1
    set -- "$dirpath"/*.md
    [ -f "$dirpath/${dirpath##*/}.md" ] && [ "$#" -eq 1 ]' sh {} \; -print

Ở trên sẽ tìm thấy tất cả các thư mục bên dưới thư mục hiện tại (bao gồm cả thư mục hiện tại) và sẽ thực thi một tập lệnh shell ngắn cho mỗi thư mục.

Mã shell sẽ kiểm tra xem có tệp đánh dấu nào có cùng tên với thư mục bên trong thư mục hay không và liệu đây có phải là *.mdtên duy nhất trong thư mục đó không. Nếu một tệp như vậy tồn tại và nếu đó là *.mdtên duy nhất , tập lệnh shell nội tuyến sẽ thoát với trạng thái thoát không. Nếu không, nó thoát với trạng thái thoát khác không (báo hiệu lỗi).

Các set -- "$dirpath"/*.mdbit sẽ thiết lập các tham số vị trí vào danh sách các tên đường dẫn phù hợp với mô hình (phù hợp với bất kỳ tên với một hậu tố .mdtrong thư mục). Sau đó chúng ta có thể sử dụng $#để xem có bao nhiêu trận đấu chúng ta nhận được từ điều này.

Nếu shell script thoát thành công, -printsẽ in đường dẫn đến thư mục tìm thấy.

Phiên bản nhanh hơn một chút, sử dụng ít lệnh hơn của tập lệnh nội tuyến, nhưng điều đó không cho phép bạn làm nhiều hơn với chính tên đường dẫn được tìm thấy find(tập lệnh nội tuyến có thể được mở rộng thêm):

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

Các lệnh tương tự nhưng không quan tâm đến việc có các .mdtệp khác trong thư mục hay không:

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

Xem thêm:


4

Hoặc

find . -type d -exec sh -c '[ -f "$1/${1##*/}.md" ]' find-sh {} \; -print

hoặc là

find . -type d -exec sh -c '
  for d do
    [ -f "$d/${d##*/}.md" ] && printf "%s\n" "$d"
  done' find-sh {} +

Để tránh chạy một shtệp trên mỗi tệp.

Các find-shlà một chuỗi tùy ý đó trở thành của vỏ 0 vị trí tham số $0- làm cho nó một cái gì đó đáng nhớ có thể giúp gỡ rối trong trường hợp các lỗi gặp gỡ vỏ (những người khác có thể đề nghị sử dụng đồng bằng shhoặc thậm chí _như một mặc định "bỏ qua" tham số).


0

Đây là của tôi. Tôi đã thêm một số thư mục và tập tin để xác minh. Tôi cũng thấy chán, vì vậy tôi đã thêm lần sửa đổi cuối cùng và MD5. Có lẽ bạn đang tìm kiếm bản sao.

GREEN='\033[0;32m'
RED='\033[0;31m'
NC='\033[0m'

mkdir -pv {Pear,Grape,Raisin,Plaintain}/{DragonFruit,Nababa,Strawberry,Grape,Raisin}
touch {Pear,Grape,Raisin,Plaintain}/{DragonFruit,Nababa,Strawberry,Grape,Raisin}/{Strawberry,Grape,Raisin}.md

for dir in $(find ./ -type d)
do
    dirname="${dir##*/}"
    fname="${dirname}.md"
    if [ -f "${dir}/${fname}" ]
    then
        STAT=$(stat --printf="%y %s" "${dir}/${fname}")
        STAT="${STAT:0:19}"
        MD5=$(md5sum "${dir}/${fname}")
        MD5="${MD5:0:32}"
        printf "${GREEN}%-60s${NC}%-40s%-40s\n" "'${dir}/${fname}' exists" "$STAT" "$MD5"
    else
        echo -e "${RED}'${dir}/${fname}' doesn't exist${NC}"
    fi
done

'.//.md' doesn't exist
'./Raisin/Raisin.md' doesn't exist
'./Raisin/Raisin/Raisin.md' exists                          2019-08-07 19:54:09      a3085274bf23c52c58dd063faba0c36a
'./Raisin/Nababa/Nababa.md' doesn't exist
'./Raisin/Strawberry/Strawberry.md' exists                  2019-08-07 19:54:09      3d2eca1d4a3c539527cb956affa8b807
'./Raisin/Grape/Grape.md' exists                            2019-08-07 19:54:09      f577b20f93a51286423c1d8973973f01
'./Raisin/DragonFruit/DragonFruit.md' doesn't exist
'./Pear/Pear.md' doesn't exist
'./Pear/Raisin/Raisin.md' exists                            2019-08-07 19:54:09      61387f5d87f125923c2962b389b0dd67
'./Pear/Nababa/Nababa.md' doesn't exist
'./Pear/Strawberry/Strawberry.md' exists                    2019-08-07 19:54:09      02c9e39ba5b77954082a61236f786d34
'./Pear/Grape/Grape.md' exists                              2019-08-07 19:54:09      43e85d5651cac069bba8ba36e754079d
'./Pear/DragonFruit/DragonFruit.md' doesn't exist
'./Apple/Apple.md' doesn't exist
'./Apple/Banana/Banana.md' exists                           2019-08-07 19:54:09      a605268f3314411ec360d7e0dd234960
'./Apple/Banana/Papaya/Papaya.md' exists                    2019-08-07 19:54:09      e759a879942fe986397e52b7ba21a9ff
'./Apple/Banana/Orange/Orange.md' exists                    2019-08-07 19:54:09      127618fe9ab73937836b809fa0593572
'./Plaintain/Plaintain.md' doesn't exist
'./Plaintain/Raisin/Raisin.md' exists                       2019-08-07 19:54:09      13ed6460f658ca9f7d222ad3d07212a2
'./Plaintain/Nababa/Nababa.md' doesn't exist
'./Plaintain/Strawberry/Strawberry.md' exists               2019-08-07 19:54:09      721d7a5a32f3eacf4b199b74d78b91f0
'./Plaintain/Grape/Grape.md' exists                         2019-08-07 19:54:09      0bdaff592bbd9e2ed5fac5a992bb3566
'./Plaintain/DragonFruit/DragonFruit.md' doesn't exist
'./Grape/Grape.md' doesn't exist
'./Grape/Raisin/Raisin.md' exists                           2019-08-07 19:54:09      aa5d4c970e7b4b6dc35cd16d1863b5bb
'./Grape/Nababa/Nababa.md' doesn't exist
'./Grape/Strawberry/Strawberry.md' exists                   2019-08-07 19:54:09      8b02f8273bbff1bb3162cb088813e0c9
'./Grape/Grape/Grape.md' exists                             2019-08-07 19:54:09      5593d7d6fdcbb48ab5901ba30469bbe8

-1

Điều này sẽ đòi hỏi một chút logic.

for fd in `find . -type d`; do
  dir=${fd##*/}
  if [ -f ${fd}/${dir}.md ]; then
    ls ${fd}/${dir}.md
  fi
done

Bạn cũng có thể điều chỉnh nó để phù hợp với một lớp lót bằng cách sử dụng các khối mã.

EDIT: Bash là khó khăn. basedirkhông phải là một lệnh, dirnamekhông làm những gì tôi nghĩ nó đã làm, vì vậy hãy mở rộng tham số.


Đó là bởi vì tôi dường như không thể nhớ các lệnh bash hoặc cách chúng hoạt động.
Zach Sanchez

dirnamelà lệnh bạn đang tìm kiếm và các bài tập không thể có khoảng trắng xung quanh =.
Kusalananda

Nhận thấy rằng khá nhanh sau khi nó được chỉ ra, và các không gian là một lỗi đánh máy.
Zach Sanchez

Điều này phá vỡ trên tất cả các loại tên tập tin, đặc biệt là với không gian. Đừng phân tích đầu ra của ls hoặc tìm . Xem các câu trả lời khác ở đây để biết cách tiếp cận hợp lý.
Gilles 'SO- ngừng trở nên xấu xa'

Ah, chết tiệt, bạn đúng, tôi đã nghĩ rằng vòng lặp for sẽ liệt kê theo dòng mới, không phải bởi khoảng trắng tùy ý. Tôi phá vỡ quy tắc đó mọi lúc vì tôi hiếm khi gặp các tệp hoặc thư mục có khoảng trắng, xấu của tôi.
Zach Sanchez
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.