Temukan hanya folder yang berisi File dengan nama yang sama dengan Folder

8

Saya ingin menemukan semua subfolder, yang berisi file penurunan harga dengan nama yang sama (dan ekstensi .md).

Misalnya: Saya ingin Menemukan subfolder berikut:

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
  • Catatan: Mungkin ada file atau subdirektori lain dalam direktori.

Ada saran?


Solusi untuk masalah ini dapat diuji menggunakan kode berikut:

#!/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 "$@"
Nikhil
sumber
1
Mengenai komentar terakhir Anda. Perhatikan bahwa beberapa jawaban melakukan hal yang berbeda dari yang lain. Tambang dan Stéphane ini misalnya, ditafsirkan pertama Anda "Catatan" sebagai "jika ada file penurunan harga lain di direktori apapun , tidak kembali direktori" sementara yang lain tidak (sejauh yang saya bisa melihat). Selain itu, hanya Anda yang dapat memilih jawaban yang paling membantu Anda . Jawaban di sini akan terus menerima suara naik dan turun setelah Anda menerima jawaban, tergantung pada apa yang dianggap paling bermanfaat oleh pembaca lain.
Kusalananda
Ketika Anda mengatakan "Folder yang berisi file penurunan harga yang namanya berbeda tidak boleh ditemukan," apakah Anda bermaksud mengecualikan direktori dengan keduanya? Misalnya jika Anda memiliki foo/foo.mddan foo/bar.mdharus foodimasukkan atau dikecualikan?
Kevin
@ Kevin Dalam contoh yang Anda berikan, saya bermaksud menyertakan foo. Namun sayangnya banyak orang menafsirkan dengan cara lain dan mereka membenarkan hal itu. Jadi, saya pikir saya tidak jelas dalam komunikasi. Jadi, saya menerima jawaban yang tidak termasuk foo.
Nikhil
Jika Anda menggunakan -printfdengan find, Anda bisa mendapatkan bagian apa pun dari pertandingan yang Anda inginkan, lihat hasil edit saya
Thor

Jawaban:

13

Dengan asumsi file Anda masuk akal, yaitu tidak perlu untuk -print0dll. Anda dapat melakukan ini dengan GNU temukan seperti ini:

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

Keluaran:

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

Jika Anda hanya ingin nama direktori, tambahkan -printfargumen:

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

Output saat dijalankan pada data uji Anda yang diperbarui:

GeneratedTest/A/AA
GeneratedTest/A
GeneratedTest/C/CC2
GeneratedTest/C/CC1
GeneratedTest/B
Thor
sumber
Bahkan tanpa GNU temukan:find . -type f | egrep '.*/([^/]+)/\1\.md$'
Jim L.
3
@ Jim. Kecuali bahwa menyalurkannya ke alat yang berorientasi garis akan merusak beberapa karakter dalam nama file, seperti baris baru.
Kusalananda
1
@ Kusalananda Setuju, namun, jawaban khusus ini didasarkan pada file "bernama masuk akal" yang tidak memerlukan print0.
Jim L.
@Thor %hdi printf digunakan untuk tipe data int untuk diformat. Referensi: string format printf - Wikipedia . Bisakah Anda jelaskan bagian itu? Bagaimana %hcara digunakan di sini?
Nikhil
@Nikhil: Tidak dengan find, lihat bagian 3.2.2.1 di manual untuk detail lebih lanjut.
Thor
6

Pada sistem GNU, Anda dapat melakukan sesuatu seperti:

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}'
Stéphane Chazelas
sumber
3
maukah Anda memasukkan kembali solusi zsh yang Anda usulkan sebagai alternatif? akan sangat membantu bagi kita yang mencoba belajar lebih banyak tentang zsh
steeldriver
Mengingat bahwa jawaban ini menerima lebih banyak suara: Bagi mereka yang memilih jawaban ini, dapatkah Anda menjelaskan mengapa ini lebih baik daripada yang lain? Itu akan membantu saya memilih jawaban yang paling cocok.
Nikhil
Stéphane, saya setuju dengan steeldriver. Sebutkan zshsolusi sebelumnya (saya yakin, ada dua upvotes), dan jangan ragu untuk menunjukkan kekurangan di dalamnya yang mungkin mendorong Anda untuk menghapusnya.
Kusalananda
1
@steeldriver, dalam pendekatan zsh saya (seperti Anda) telah melewatkan bagian dari persyaratan bahwa dir yang berisi file md lainnya harus dihilangkan.
Stéphane Chazelas
@ StéphaneChazelas OP baru saja mengklarifikasi dalam komentar yang sebenarnya dia maksudkan untuk dimasukkan, itu hanya diutarakan dengan buruk dan orang-orang menganggapnya terlalu harfiah.
Kevin
6
find . -type d -exec sh -c '
    dirpath=$1
    set -- "$dirpath"/*.md
    [ -f "$dirpath/${dirpath##*/}.md" ] && [ "$#" -eq 1 ]' sh {} \; -print

Di atas akan menemukan semua direktori di bawah direktori saat ini (termasuk direktori saat ini) dan akan menjalankan skrip shell pendek untuk masing-masing.

Kode shell akan menguji apakah ada file penurunan harga dengan nama yang sama dengan direktori di dalam direktori, dan apakah ini satu-satunya *.mdnama dalam direktori itu. Jika file seperti itu ada dan jika itu satu-satunya *.mdnama, skrip inline shell keluar dengan status keluar nol. Kalau tidak, ia keluar dengan status keluar non-nol (kegagalan pensinyalan).

The set -- "$dirpath"/*.mdbit akan mengatur parameter posisi ke daftar nama path cocok dengan pola (sesuai dengan nama apapun dengan akhiran .mddalam direktori). Kami kemudian dapat menggunakan $#nanti untuk melihat berapa banyak pertandingan yang kami dapatkan dari ini.

Jika skrip shell berhasil keluar, -printakan mencetak jalur ke direktori yang ditemukan.

Versi yang sedikit lebih cepat yang menggunakan lebih sedikit pemanggilan skrip inline, tetapi itu tidak membuat Anda berbuat lebih banyak dengan nama path yang ditemukan finditu sendiri (skrip inline mungkin akan diperluas lebih lanjut):

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

Perintah yang sama tetapi tanpa peduli apakah ada .mdfile lain di direktori:

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

Lihat juga:

Kusalananda
sumber
4

Antara

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

atau

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

Untuk menghindari menjalankan satu shper file.

Ini find-shadalah string arbitrer yang menjadi parameter posisi zeroth pada shell $0- membuatnya sesuatu yang mudah diingat dapat membantu debugging jika shell menemukan kesalahan (orang lain mungkin menyarankan menggunakan plain shatau bahkan _sebagai parameter "skip" default).

Steeldriver
sumber
0

Ini milik saya. Saya menambahkan beberapa direktori dan file untuk memverifikasi. Saya juga bosan, jadi saya menambahkan waktu modifikasi dan MD5 terakhir. Mungkin Anda sedang mencari duplikat.

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
pengguna208145
sumber
-1

Ini akan membutuhkan sedikit logika.

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

Anda juga dapat mengadaptasinya agar sesuai dengan satu liner dengan menggunakan blok kode.

EDIT: Bash sulit. basedirbukan perintah, dirnametidak melakukan apa yang saya pikir itu lakukan, jadi mari kita lanjutkan dengan ekspansi parameter.

Zach Sanchez
sumber
Itu karena saya tampaknya tidak dapat mengingat perintah bash atau cara kerjanya.
Zach Sanchez
dirnameadalah perintah yang Anda cari, dan tugas tidak dapat memiliki ruang di sekitar =.
Kusalananda
Mengetahui hal itu dengan cepat setelah ditunjukkan, dan spasi adalah kesalahan ketik.
Zach Sanchez
Ini memecah semua jenis nama file, terutama dengan spasi. Jangan parsing output ls atau find . Lihat jawaban lain di sini untuk pendekatan yang masuk akal.
Gilles 'SANGAT berhenti menjadi jahat'
Ah, sial, kau benar, aku akan mengira perulangan for akan dihitung dengan baris baru, bukan dengan spasi putih sewenang-wenang. Saya melanggar aturan itu sepanjang waktu karena saya jarang menemukan file atau direktori dengan spasi, salah saya.
Zach Sanchez