Direktori dengan dua atau lebih file

11

Saya ingin mencari subdirektori dari direktori saat ini, yang (yaitu subdirektori) berisi 2 atau lebih file biasa.

Saya tidak tertarik pada direktori yang mengandung kurang dari 2 file, juga di direktori yang hanya berisi subdirektori.

porton
sumber

Jawaban:

12

Berikut adalah pendekatan yang sama sekali berbeda berdasarkan pada GNU finddan uniq. Ini jauh lebih cepat dan lebih ramah CPU daripada jawaban berdasarkan mengeksekusi perintah shell yang menghitung file untuk setiap direktori yang ditemukan.

find . -type f -printf '%h\n' | sort | uniq -d

The findperintah mencetak direktori dari semua file dalam hirarki dan uniqhanya menampilkan direktori yang muncul setidaknya dua kali.

xienne
sumber
2
Anda seharusnya tidak melewatkan output dari find. Dalam hal ini, karena GNU findakan memotong-motong nama direktori yang memiliki karakter yang tidak dapat dicetak di lokal saat ini (seperti "ä" di C locale). Lihat juga unix.stackexchange.com/questions/321697/…
Kusalananda
4
@ Kusalananda, tidak ketika output tidak pergi ke tty. Di sini, satu-satunya masalah adalah dengan karakter baris baru, yang dapat Anda perbaiki dengan menggunakan-printf '%h\0' | sort -z | uniq -zd | xargs -r0 ...
Stéphane Chazelas
6
find . -type d \
    -exec sh -c 'c=0; for n in "$1"/*; do [ -f "$n" ] && [ ! -h "$n" ] && c=$(( c + 1 )); done; [ "$c" -ge 2 ]' sh {} ';' \
    -print

Ini akan menemukan semua nama di atau di bawah direktori saat ini dan kemudian menyaring semua nama yang bukan nama direktori.

Nama direktori yang tersisa akan diberikan ke skrip pendek ini:

c=0
for n in "$1"/*; do
    [ -f "$n" ] && [ ! -h "$n" ] && c=$(( c + 1 ))
done

[ "$c" -ge 2 ]

Script ini akan menghitung jumlah file biasa (melewatkan tautan simbolik) di direktori yang diberikan sebagai argumen baris perintah pertama (dari find). Perintah terakhir dalam skrip adalah tes untuk melihat apakah hitungannya 2 atau lebih besar. Hasil tes ini adalah nilai balik (status keluar) dari skrip.

Jika tes berhasil, -printakan menyebabkan finduntuk mencetak jalur ke direktori.

Untuk juga mempertimbangkan file tersembunyi (file yang namanya dimulai dengan titik), ubah sh -cskrip agar tidak mengatakan

for n in "$1"/*; do

untuk

for n in "$1"/* "$1"/.*; do

Pengujian:

$ tree
.
`-- test
    |-- a
    |-- dir1
    |   |-- a
    |   |-- b
    |   `-- c
    `-- dir2
        |-- dira
        |-- dirb
        |   |-- file-1
        |   `-- file-2
        `-- dirc

6 directories, 6 files

$ find . -type d -exec sh -c 'c=0; for n in "$1"/*; do [ -f "$n" ] && [ ! -h "$n" ] && c=$(( c + 1 )); done; [ "$c" -ge 2 ]' sh {} ';' -print
./test/dir1
./test/dir2/dirb
Kusalananda
sumber
Solusi Anda tidak menghitung file dengan nama yang dimulai dengan titik. Anda juga harus menginisialisasi c = 0 untuk menghindari pesan kesalahan dengan direktori yang tidak mengandung file apa pun.
xhienne
@xhienne Saya menganggap file tersembunyi dan akan menambahkan catatan tentang itu. Tidak ada kesalahan jika tidak ada file biasa dalam direktori karena [ "" -ge 2 ]ini adalah tes yang valid.
Kusalananda
Tidak yakin bagaimana Anda mendefinisikan "valid". POSIX membutuhkan arg1 untuk menjadi nilai integer. dash, bash --posixdan testsemua menampilkan pesan kesalahan dan keluar dengan 2 (yaitu "Terjadi kesalahan")
xhienne
@xhienne Ah, saya menguji pada sistem yang kshberjalan seperti mas sh. Akan segera diubah. Terima kasih telah menyodok saya! :-)
Kusalananda
Juga, [ -f ... ]tautan simbolik dereferensi. Anda harus menambahkan tes untuk menghilangkannya karena pertanyaan menentukan bahwa hanya file biasa yang harus dihitung.
xhienne
6

Dengan bantuan jawaban Gilles pada SU dan kebalikannya dan beberapa modifikasi, inilah yang Anda butuhkan.

find . -type d -exec sh -c 'set -- "$1"/*;X=0; 
    for args; do [ -f "$args" ] && X=$((X+1)) ;done; [ "$X" -gt 1 ] ' _ {} \; -print

Pohon direktori.

.
├── test
│   ├── dir1
│   │   ├── a
│   │   ├── b
│   │   └── c
│   ├── dir2
│   │   ├── dira
│   │   │   └── a file\012with\012multiple\012line
│   │   ├── dirb
│   │   │   ├── file-1
│   │   │   └── file-2
│   │   └── dirc
│   ├── diraa
│   ├── dirbb
│   ├── dircc
│   └── x
│   └── x1
│   └── x2
└── test2
    ├── dir3
    └── dir4

Hasil:

./test
./test/dir1
./test/dir2/dirb
αғsнιη
sumber
Saya punya ini pada awalnya juga, tetapi Anda akan memiliki masalah dengan direktori yang berisi banyak subdirektori dan file. Itu juga tidak membuang direktori hanya berisi subdirektori.
Kusalananda
Itu tidak benar-benar menyelesaikannya. Ia menemukan baik testdan dir2direktori dalam pengujian saya (lihat jawaban saya).
Kusalananda
Berfungsi untuk contoh Anda, tetapi tambahkan test/x1dan test/x2sebagai file juga ... $1dan $2akan menjadi direktori untuk test, dan direktori akan dilewatkan.
Kusalananda
@ Kusalananda Tidak mungkin saya menemukan kecuali apa yang Anda jawab, saya mencoba mengubah beberapa bagian dari perintah saya untuk tidak menjadi duplikat yang tepat dari Anda (saya tidak mengecualikan file tersembunyi seperti yang Anda lakukan), saya minta maaf.
αғsнιη
1
Jangan khawatir apa pun :-)
Kusalananda
3

Lain find+ wcpendekatan:

find path/currdir -maxdepth 1 -type d ! -empty ! -path "path/currdir" \
-exec sh -c 'count=$(find "$1" -maxdepth 1 -type f | wc -l); [ $count -ge 2 ]' _ {} \; -print

  • path/currdir - path ke direktori Anda saat ini

  • -maxdepth 1- pertimbangkan hanya subfolder anak langsung

  • ! -empty - abaikan subfolder kosong

  • ! -path "path/currdir" - abaikan jalur direktori saat ini

  • count=$(find "$1" -maxdepth 1 -type f | wc -l)- countditugaskan dengan jumlah file untuk setiap subfolder yang ditemukan

  • [ $count -ge 2 ] ... -print - cetak nama / jalur subfolder yang berisi 2 atau lebih file biasa

RomanPerekhrest
sumber