Bagaimana menemukan file di subdir dan mengurutkannya dengan nama file dalam satu perintah?

9

Hasil temuan normal menggunakan find . ! -path "./build*" -name "*.txt":

./tool/001-sub.txt
./tool/000-main.txt
./zo/001-int.txt
./zo/id/002-and.txt
./as/002-mod.txt

dan ketika disortir dengan sort -n:

./as/002-mod.txt
./tool/000-main.txt
./tool/001-sub.txt
./zo/001-int.txt
./zo/id/002-and.txt

namun output yang diinginkan adalah:

./tool/000-main.txt
./zo/001-int.txt
./tool/001-sub.txt
./zo/id/002-and.txt
./as/002-mod.txt

yang berarti output diurutkan berdasarkan nama file saja , tetapi informasi folder harus dipertahankan sebagai bagian dari output.

Sunting : Jadikan contoh lebih rumit karena struktur subdirektori dapat mencakup lebih dari satu level.

unode
sumber
2
Lihat pertanyaan ini yang saya tanyakan pada SO: stackoverflow.com/questions/3222810/…
camh
@camh - jika memungkinkan saya hanya ingin menggunakan perintah unix. Bagaimanapun, pertanyaan saya adalah duplikat dari pertanyaan Anda. Bisakah Anda mentransfer solusi terbaik ke utas ini (simpan tautan ke aslinya) sehingga saya dapat menandai sebagai solusinya?
unode
Jika @Shawn membuat perubahan yang saya sarankan dalam komentar saya (gunakan -printfbukan awk), saya pikir itu adalah solusi terbaik. Saya telah mengerjakan ulang implementasi asli saya untuk menggunakan metode ini.
camh

Jawaban:

9

Anda perlu mengurutkan berdasarkan bidang terakhir (mempertimbangkan /sebagai pemisah bidang). Sayangnya, saya tidak bisa memikirkan alat yang bisa melakukan ini ketika jumlah bidang bervariasi (jika hanya sort -kbisa mengambil nilai negatif).

Untuk menyiasatinya, Anda harus melakukan penghias-sort-undecorate. Yaitu, ambil nama file dan letakkan di awal diikuti oleh pemisah bidang, lalu lakukan pengurutan, lalu hapus kolom pertama dan pemisah bidang.

find . ! -path "./build*" -name "*.txt" |\
    awk -vFS=/ -vOFS=/ '{ print $NF,$0 }' |\
    sort -n -t / |\
    cut -f2- -d/

Bahwa awkperintah mengatakan pemisah lapangan FS diatur ke /; ini mempengaruhi cara membaca bidang. The pemisah output field OFS juga diatur untuk /; ini mempengaruhi cara mencetak catatan. Pernyataan berikutnya mengatakan cetak kolom terakhir ( NFadalah jumlah bidang dalam catatan, sehingga juga merupakan indeks dari bidang terakhir) serta seluruh catatan ( $0adalah seluruh catatan); itu akan mencetaknya dengan OFS di antara mereka. Kemudian daftarnya sortdiedit, diperlakukan /sebagai pemisah bidang - karena kita memiliki nama file pertama dalam catatan, itu akan mengurutkan berdasarkan itu. Kemudian cuthanya bidang cetak 2 yang berakhir, yang lagi diperlakukan /sebagai pemisah bidang.

Shawn J. Goff
sumber
3
Karena ini dengan find (1), Anda dapat melewati bagian awk dan menggunakan-printf '%f/%p\n'
camh
memang pengaturan kami sedikit lebih rumit. Itu tidak termasuk kedalaman subdir variabel. Mengedit pertanyaan untuk mencerminkan fakta ini. Saya minta maaf karena tidak memasukkan ini pada awalnya.
unode
1
@Unode: Solusi Shawn menangani kedalaman variabel dengan baik, itu solusi kanonik untuk masalah ini (hingga variasi kecil).
Gilles 'SANGAT berhenti menjadi jahat'
4

Saya akan menggunakan file '-printf' untuk menampilkan nama dan jalur, mengurutkan berdasarkan nama, dan memotong nama pada langkah terakhir. '###' hanyalah penanda, untuk membantu memotong.

find -name "*.txt" -printf "%f###%p\n" | sort -n | sed 's/.*###//'

% f mencetak nama file,% p seluruh path.

Saya menyederhanakan perintah find untuk membuatnya menjadi satu baris, tentu saja Anda akan meninggalkan ! -path "./build*"bagian itu.

Pengguna tidak diketahui
sumber
3

Dalam zsh ≥4.3.10:

print -l -- **/*.txt~build*(oe\''REPLY=${REPLY:t}'\')
  • **/*.txtcocok *.txtdi direktori saat ini dan subdirektori secara rekursif .
  • ~build* mengecualikan kecocokan yang teksnya dimulai dengan build*(seperti ! -path './build*'). (Anda perlu setopt extended_globterlebih dahulu.)
  • (oe\''…'\')adalah kualifikasi glob sorting . REPLY=…membangun string untuk disortir dari string untuk kembali.
  • ${REPLY:t}adalah basename ( “ekor”) dari jalan.
Gilles 'SANGAT berhenti menjadi jahat'
sumber
Banyak sihir yang digabungkan. Menarik tetapi kami terbatas pada sintaks sh. +1
hapus kode