Menemukan semua file dengan ekstensi yang diberikan yang nama dasarnya adalah nama direktori induk

9

Saya ingin secara rekursif mencari setiap *.pdffile dalam direktori ~/fooyang nama dasarnya cocok dengan nama direktori induk file.

Sebagai contoh, misalkan struktur direktori ~/footerlihat seperti ini

foo
├── dir1
│   ├── dir1.pdf
│   └── dir1.txt
├── dir2
│   ├── dir2.tex
│   └── spam
│       └── spam.pdf
└── dir3
    ├── dir3.pdf
    └── eggs
        └── eggs.pdf

Menjalankan perintah yang saya inginkan akan kembali

~/foo/dir1/dir1.pdf
~/foo/dir2/spam/spam.pdf
~/foo/dir3/dir3.pdf
~/foo/dir3/eggs/eggs.pdf

Apakah ini mungkin menggunakan findatau utilitas inti lainnya? Saya menganggap ini bisa dilakukan dengan menggunakan -regexopsi findtapi saya tidak yakin bagaimana menulis pola yang benar.

Brian Fitzpatrick
sumber
Ya, saya akan membuat contoh sekarang.
Brian Fitzpatrick
1
@Inian Menambahkan contoh. Apakah ini membantu?
Brian Fitzpatrick

Jawaban:

16

Dengan GNU find:

find . -regextype egrep -regex '.*/([^/]+)/\1\.pdf'
  • -regextype egrep gunakan regex style egrep.
  • .*/ cocok dengan sutradara grand parent.
  • ([^/]+)/ mencocokkan dir induk dalam suatu grup.
  • \1\.pdfgunakan backreferenceuntuk mencocokkan nama file sebagai dir induk.

memperbarui

Satu (saya sendiri) mungkin berpikir itu .*cukup serakah, tidak perlu dikecualikan /dari pencocokan orang tua:

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

Perintah di atas tidak akan berfungsi dengan baik, karena itu berarti ./a/b/a/b.pdf:

  • .*/ cocok ./
  • (.+)/ cocok a/b/
  • \1.pdf cocok a/b.pdf
dedowsdi
sumber
Sangat keren. Seandainya saya bisa regex ini dengan baik.
Brian Fitzpatrick
Atau find . -regex '.*/\([^/]*\)/\1\.pdf'bahkan itu akan bekerja dengan BSD find.
Stéphane Chazelas
7

Varian loop tradisional find .. -exec sh -c ''untuk menggunakan konstruksi shell agar sesuai dengan nama dasar dan jalur langsung di atas akan dilakukan di bawah.

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

Untuk rincian parameter ekspansi individu

  • fileberisi path lengkap .pdffile yang dikembalikan dari findperintah
  • "${file##*/}"hanya berisi bagian setelah yang terakhir /yaitu hanya nama file
  • "${file%/*}"berisi path hingga final /yaitu kecuali bagian nama samaran dari hasil
  • "${path##*/}"berisi bagian setelah yang terakhir /dari pathvariabel, yaitu path folder langsung di atas nama file
  • "${base%.*}"berisi bagian dari nama samaran dengan .pdfekstensi dihapus

Jadi jika nama samaran tanpa ekstensi cocok dengan nama folder langsung di atas, kami mencetak jalurnya.

Inian
sumber
7

Kebalikan dari jawaban Inian , yaitu mencari direktori, dan kemudian melihat apakah mereka memegang file dengan nama tertentu.

Yang berikut ini mencetak nama path dari file yang ditemukan relatif terhadap direktori 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##*/}akan diganti oleh bagian nama file dari jalur direktori, dan bisa diganti oleh $(basename "$dirpath").

Untuk orang yang menyukai sintaks hubung singkat:

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

Manfaat melakukannya dengan cara ini adalah Anda mungkin memiliki lebih banyak file PDF daripada direktori. Jumlah tes yang terlibat berkurang jika seseorang membatasi kueri dengan jumlah yang lebih kecil (jumlah direktori).

Misalnya, jika satu direktori berisi 100 file PDF, ini hanya akan mencoba untuk mendeteksi salah satu dari mereka daripada menguji nama-nama semua 100 file terhadap direktori.

Kusalananda
sumber
3

dengan zsh:

printf '%s\n' **/*/*.pdf(e@'[[ $REPLY:t = $REPLY:h:t.pdf ]]'@)

Waspadai bahwa sementara **/tidak akan mengikuti symlink, */akan.

Stéphane Chazelas
sumber
2

Itu tidak ditentukan, tetapi di sini adalah solusi tanpa ekspresi reguler jika ada yang tertarik.

Kita bisa menggunakan find . -type funtuk hanya mendapatkan file, lalu memanfaatkan dirnamedan basenamemenulis persyaratan. Utilitas memiliki perilaku berikut:

$ find . -type f
./dir2/spam/spam.pdf
./dir2/dir2.tex
./dir3/dir3.pdf
./dir3/eggs/eggs.pdf
./dir1/dir1.pdf
./dir1/dir1.txt

basenamemengembalikan hanya nama file setelah yang terakhir /:

$ for file in $(find . -type f); do basename $file; done
spam.pdf
dir2.tex
dir3.pdf
eggs.pdf
dir1.pdf
dir1.txt

dirnamememberikan seluruh jalan ke final /:

$ for file in $(find . -type f); do dirname $file; done
./dir2/spam
./dir2
./dir3
./dir3/eggs
./dir1
./dir1

Oleh karena itu, basename $(dirname $file)berikan direktori induk file.

$ for file in $(find . -type f); do basename $(dirname $file) ; done
spam
dir2
dir3
eggs
dir1
dir1

Larutan

Gabungkan yang di atas untuk membentuk conditional "$(basename $file)" = "$(basename $(dirname $file))".pdf, maka hanya cetak setiap hasil dari findjika conditional mengembalikan true.

$ 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

Dalam contoh di atas, kami telah menambahkan direktori / file dengan spasi dalam nama untuk menangani kasus itu (terima kasih kepada @Kusalananda di komentar)

pengguna1717828
sumber
Sayangnya ini akan merusak nama file seperti Final Thesis.pdf(dengan spasi).
Kusalananda
@ Kusalananda Diperbaiki.
user1717828
0

Saya mengambil bash globbing, loop sederhana atas tes string setiap hari selama program Find . Panggil saya irasional, dan meskipun mungkin suboptimal kode sederhana seperti melakukan trik untuk saya: dapat dibaca dan digunakan kembali, bahkan memuaskan !. Izinkan saya menyarankan kombinasi:

• pesta globstar : for f in ** ; do ... ** loop atas setiap file dalam direktori saat ini dan semua subfolder .. untuk memeriksa status globstar dalam sesi Anda saat ini: shopt -p globstar. Untuk globstar mengaktifkan: shopt -s globstar.

• utlity "file" : if [[ $(file "$f") =~ pdf ]]; then ... untuk memeriksa format file aktual untuk pdf - lebih kuat daripada hanya menguji ekstensi file

• basename, dirname : untuk membandingkan nama file dengan nama direktori tepat di atasnya. basenamemengembalikan nama file - dirnamemengembalikan seluruh jalur direktori - menggabungkan dua fungsi untuk hanya mengembalikan satu direktori yang berisi file yang cocok. Saya menempatkan masing-masing dalam variabel ( _mydir dan _myf ) untuk kemudian melakukan tes sederhana menggunakan = ~ untuk pencocokan string.

Satu subtilitas: hapus sembarang "titik" dalam nama file untuk menghindari pencocokan nama file dengan direktori saat ini yang pintasannya juga "." - Saya menggunakan subtitusi string langsung pada variabel _myf : ${_myf//./}- tidak terlalu elegan tetapi berfungsi. Pertandingan positif akan kembali jalur masing-masing file - bersama-sama dengan path lengkap dari folder saat ini dengan mendahului output dengan: $(pwd)/.

Kode

for f in ** ; do
  if [[ $(file "$f") =~ PDF ]]; then
    _mydir="$(basename $(dirname $f))" ; 
    _myf="$(basename $f)" ; 
    [[ "${_myf//./}" =~ "$_mydir" ]] && echo -e "$(pwd)/$f" ; 
  fi ; 
done
docgyneco69
sumber