デフォルト名がファイルの親ディレクトリ名と一致するディレクトリ内の*.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 egrep
egrepスタイルの正規表現を使用してください。.*/
祖父母様マッチング案内です。([^/]+)/
グループの親ディレクトリと一致します。\1\.pdf
backreference
ファイル名を親ディレクトリと一致させるために使用されます。
修正する
誰か(自分自身)は、これが十分に貪欲であり、親一致から除外する必要はないと考える.*
ことができます。/
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
.pdf
find
コマンドから返されたファイルのフルパスを含みます。"${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に感謝します)