解決策

解決策

デフォルト名がファイルの親ディレクトリ名と一致するディレクトリ内の*.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?それとも別のコアユーティリティを使用できますか? optionsを使ってこれを行うことは可能だと思います-regexが、find正しいパターンを作成する方法がわかりません。

答え1

GNUの使用find:

find . -regextype egrep -regex '.*/([^/]+)/\1\.pdf'
  • -regextype egrepegrepスタイルの正規表現を使用してください。
  • .*/祖父母様マッチング案内です。
  • ([^/]+)/グループの親ディレクトリと一致します。
  • \1\.pdfbackreferenceファイル名を親ディレクトリと一致させるために使用されます。

修正する

誰か(自分自身)は、これが十分に貪欲であり、親一致から除外する必要はないと考える.*ことができます。/

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

上記のコマンドは数学的なので正しく機能しません./a/b/a/b.pdf

  • .*/マッチ./
  • (.+)/マッチa/b/
  • \1.pdfマッチa/b.pdf

答え2

find .. -exec sh -c ''基本名と上記の直接パスを一致させるためにシェル構成を使用する既存のループのバリエーションは、次のことを行います。

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.pdffindコマンドから返されたファイルのフルパスを含みます。
  • "${file##*/}"最後の部分の後ろの部分のみを含めます/。つまり、ファイルのデフォルト名のみが含まれます。
  • "${file%/*}"/最終パスを含めます(結果のデフォルト名部分を除く)。
  • "${path##*/}"/pathファイルのデフォルト名の上の直接フォルダパスである最後の変数の後の部分が含まれています。
  • "${base%.*}".pdf拡張子が削除されたデフォルト名部分を含みます。

したがって、拡張子のないデフォルト名が上記のダイレクトフォルダ名と一致すると、パスが印刷されます。

答え3

~と反対イニアンの答えつまり、ディレクトリを探して特定の名前のファイルが含まれていることを確認します。

以下は、ディレクトリに基づいて見つかったファイルのパス名を出力します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個のファイル全体の名前ではなく、ファイルの1つだけを検出しようとします。

答え4

指定されていませんが、ここに興味がある人がいる場合は、正規表現のないソリューションがあります。

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最後までフルパスを提供します/

$ 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

解決策

上記の内容を組み合わせて条件を作成し、"$(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に感謝します)

関連情報