フォルダーと同じ名前のファイルを含むフォルダーのみを検索します


8

同じ名前(および拡張子.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
  • 注:ディレクトリには他のファイルまたはサブディレクトリが存在する場合があります。

助言がありますか?


この問題の解決策は、次のコードを使用してテストできます。

#!/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
最後の発言について。一部の回答は他の回答とは異なることに注意してください。鉱山とステファンのは、たとえば、「ディレクトリ内の他の値下げのファイルがあるかのようにあなたの最初の「注意」と解釈全くそのディレクトリを返さない、」他の人がいない間(私の知る限り)。それとは別に、あなただけがあなたにとって最も役立つ答えを選ぶことができます。ここでの回答は、他の読者が最も役立つと思うものに応じて、回答を受け入れた後も引き続き賛成票と反対票を受け取ります。
クサラナンダ

「名前が異なるマークダウンファイルを含むフォルダを検出しないでください」と言うとき、両方のディレクトリを除外することを意味しますか?たとえば、あなたが持っfoo/foo.mdているfoo/bar.mdfoo、含まれるべきか、除外すべきか?
ケビン

@ケビンあなたが与えた例では、私はfooを含めるつもりでした。しかし、残念ながら多くの人々が逆の解釈をしており、彼らはそれを正当化しました。だから、コミュニケーションがはっきりしないと思いました。それで、fooを含まない答えを受け入れました。
Nikhil

-printffindと一緒に使用すると、一致のどの部分でも取得できます。私の編集を参照してください
Thor

回答:


13

あなたのファイルが賢明な名前を付けられていると仮定します-print0。すなわち、等は必要ありません。これは、GNU findを使って次のように行うことができます:

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

出力:

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

ディレクトリ名のみが必要な場合は、-printf引数を追加します。

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

更新されたテストデータで実行したときの出力:

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

GNU findがなくても:find . -type f | egrep '.*/([^/]+)/\1\.md$'
Jim L.

3
@JimL。それを行指向のツールにパイプすると、改行のようにファイル名の一部の文字が壊れることを除いて。
Kusalananda

1
@Kusalananda同意しますが、この特定の回答は、不要な「適切に名前が付けられた」ファイルに基づいていますprint0
ジムL.

%hprintfの@Thor は、フォーマットされるデータのintタイプに使用されます。参考:printfのフォーマット文字列-ウィキペディア。その部分について説明してもらえますか?%hここでどのように使用されていますか?
Nikhil

@Nikhil:ではなくfind、詳細についてはマニュアルのセクション3.2.2.1を参照してください。
トール

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

3
提案されたzshソリューションを代替として再含めてもよろしいですか?それはzshの詳細情報しようとしているこれらの私たちのために役立つだろう
steeldriver

この回答がより多くの票を獲得したことを考えると、この回答を支持している人たちに、これが他の回答よりも優れている理由を具体的に教えていただけませんか。それは私が最も適切な答えを選択するのに役立ちます。
Nikhil

ステファン、私はsteeldriverに同意します。以前のzsh解決策(2つの賛成票を得たと思います)について言及し、削除するように促した可能性のある欠陥を指摘してください。
クサラナンダ

1
@steeldriver、そのzshアプローチでは、私(あなたのような)は、他のmdファイルを含むdirsを省略すべきであるという要件の一部を逃していた。
ステファンChazelas

@StéphaneChazelasOP は、彼が実際にそれらを含めることを意図していたコメントで明確にしたばかりで、言葉遣いが不十分で、人々は文字通りそれを受け入れすぎていました。
ケビン

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

上記は、現在のディレクトリ(現在のディレクトリを含む)の下にあるすべてのディレクトリを検索し、それぞれに対して短いシェルスクリプトを実行します。

シェルコードは、ディレクトリ内のディレクトリと同じ名前のマークダウンファイルがあるかどうか、およびこれが*.mdそのディレクトリで唯一の名前かどうかをテストします。そのようなファイルが存在し、それが唯一の*.md名前である場合、インラインシェルスクリプトはゼロの終了ステータスで終了します。それ以外の場合は、ゼロ以外の終了ステータスで終了します(シグナリング障害)。

このset -- "$dirpath"/*.mdビットは、定位置パラメーターをパターンに一致するパス名のリストに設定します(.mdディレクトリー内のサフィックスを持つ任意の名前に一致します)。その後、$#後でこれを使用して、一致の数を確認できます。

シェルスクリプトが正常に終了した場合、-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 {} +

同じコマンド.mdですが、ディレクトリに他のファイルがあるかどうかは気にしません:

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

以下も参照してください。


4

どちらか

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

または

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

shファイルごとに1つ実行することを避けるため。

find-shシェルのゼロ番目の位置パラメータとなる任意の文字列である$0-それを何か思い出に残るを作ることは(他の人が無地使用して提案することができる場合には、シェルの出会いエラーのデバッグに役立つことがありsh、あるいは_、パラメータを「スキップ」のデフォルトとして)。


0

こちらが私のものです。確認するディレクトリとファイルをいくつか追加しました。私も退屈だったので、最終更新日時とMD5を追加しました。多分あなたは重複を探しています。

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

これには少しロジックが必要です。

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

コードブロックを使用して、1つのライナーに収まるように調整することもできます。

編集:バッシュは難しいです。basedirはコマンドでdirnameはないので、私が思っていたようには動作しないので、パラメーターの拡張について説明しましょう。


それはどうやらbashコマンドやそれらがどのように機能するかを思い出せないからです。
ザックサンチェス

dirnameは、探しているコマンドです。割り当てには、の周りにスペースを含めることはできません=
Kusalananda

それが指摘された後、すぐにそれが判明し、スペースはタイプミスでした。
Zach Sanchez

これにより、すべての種類のファイル名、特にスペースが壊れます。lsまたはfindの出力を解析しないでください。賢明なアプローチについては、ここで他の回答を参照してください。
Gilles 'SO-悪をやめる'

ああ、くそー、あなたの言うとおり、forループは任意の空白ではなく改行で列挙すると思いました。私はいつもこのルールを破っていますが、スペースのあるファイルやディレクトリに出会うことはめったにありません。
Zach Sanchez
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.