Meratakan direktori bersarang

71

Ini mungkin sangat sederhana, tetapi saya tidak bisa mengetahuinya. Saya memiliki struktur direktori seperti ini (dir2 ada di dalam dir1):

/dir1
    /dir2 
       |
        --- file1
       |
        --- file2

Apa cara terbaik untuk 'meratakan' struktur direktur ini sedemikian rupa untuk mendapatkan file1 dan file2 di dir1 bukan dir2.

penyu
sumber

Jawaban:

75

Anda dapat melakukan ini dengan GNU finddan GNU mv:

find /dir1 -mindepth 2 -type f -exec mv -t /dir1 -i '{}' +

Pada dasarnya, cara yang berfungsi jika findmelewati seluruh pohon direktori dan untuk setiap file ( -type f) yang tidak ada di direktori tingkat atas ( -mindepth 2), ia menjalankan a mvuntuk memindahkannya ke direktori yang Anda inginkan ( -exec mv … +). The -targumen untuk mvmemungkinkan Anda menentukan direktori tujuan pertama, yang diperlukan karena +bentuk -execmenempatkan semua lokasi sumber di akhir perintah. The -imerek mvbertanya sebelum Timpa duplikat apapun; Anda dapat menggantikan -fmenimpa mereka tanpa meminta (atau -nuntuk tidak meminta atau menimpa).

Seperti yang ditunjukkan Stephane Chazelas, hal di atas hanya berfungsi dengan alat GNU (yang merupakan standar pada Linux, tetapi tidak pada kebanyakan sistem lainnya). Berikut ini agak lambat (karena memanggil mvbeberapa kali) tetapi jauh lebih universal:

find /dir1 -mindepth 2 -type f -exec mv -i '{}' /dir1 ';'
derobert
sumber
3
Diedit untuk digunakan -exec +sehingga tidak menjalankan sejumlah besar prosesmv
Random832
1
@ Random832 Dan akan kembali lagi, karena + tidak berfungsi. mvmembutuhkan tujuan sebagai argumen terakhir, tetapi + akan memiliki sumber sebagai argumen terakhir. Cari bahkan tidak akan menerima sintaks Anda mengubahnya menjadi ( find: missing argument to `-exec')
derobert
1
@ Random832 tapi saya kira mvmemiliki -tkita bisa menggunakan, jadi saya akan mengubahnya ke itu.
derobert
1
@ Tom findmencetak file tersembunyi (titik) secara default. Kedalamannya relatif terhadap direktori yang Anda lewati untuk menemukan.
derobert
1
Atau find ./dir -mindepth 2 -type f -exec mv -f '{}' ./dir ';'jika
mengganti
33

Dalam zsh:

mv dir1/*/**/*(.D) dir1

**/melintasi subdirektori secara rekursif. The kualifikasi gumpal . cocok file biasa saja, dan Dmemastikan bahwa file dot disertakan (secara default, file yang namanya dimulai dengan .dikecualikan dari wildcard cocok). Untuk membersihkan direktori yang kosong sekarang, jalankan rmdir dir1/**/*(/Dod)- /batasi ke direktori, dan odpesan kedalaman pertandingan terlebih dahulu untuk dihilangkan dir1/dir2/dir3sebelumnya dir1/dir2.

Jika panjang total nama file sangat besar, Anda dapat mengalami batasan pada panjang baris perintah. Zsh memiliki bawaan untuk mvdan rmdiryang tidak terpengaruh oleh batasan ini: jalankan zmodload zsh/filesuntuk mengaktifkannya.

Dengan hanya alat POSIX:

find dir1 -type f -exec mv {} dir1 \;
find dir1 -depth -exec rmdir {} \;

atau (lebih cepat karena tidak harus menjalankan proses terpisah untuk setiap file)

find dir1 -type f -exec sh -c 'mv "$@" dir1' _ {} +
find dir1 -depth -exec rmdir {} +
Gilles 'SANGAT berhenti menjadi jahat'
sumber
1
Ini harus menjadi jawaban yang diterima! Apalagi dengan versi zsh yang ringkas.
Adamski
3

Coba lakukan ini:

cp /dir1/dir2/file{1,2} /another/place

atau untuk setiap file yang cocok file[0-9]*dengan subdir:

cp /dir1/dir2/file[0-9]* /another/place

Lihat http://mywiki.wooledge.org/glob

Gilles Quenot
sumber
Saya seharusnya menunjukkan ini, tetapi saya harus banyak file untuk digunakan {}dalam masalah saya yang sebenarnya.
kura
Lihat solusi kedua saya
Gilles Quenot
Bingo. Terima kasih untuk bantuannya. Ini jelas merupakan solusi terbaik.
kura
2

Saya menulis dua fungsi yang dapat Anda gunakan bersama yang melakukan hal itu, Anda dapat membatasi level direktori dengan menambahkan -maxdepth $VALparameter.

# This scripts flattens the file directory
# Run this script with a folder as parameter:
# $ path/to/script path/to/folder

#!/bin/bash

rmEmptyDirs(){
    local DIR="$1"
    for dir in "$DIR"/*/
    do
        [ -d "${dir}" ] || continue # if not a directory, skip
        dir=${dir%*/}
        if [ "$(ls -A "$dir")" ]; then
            rmEmptyDirs "$dir"
        else
            rmdir "$dir"
        fi
    done
    if [ "$(ls -A "$DIR")" ]; then
        rmEmptyDirs "$DIR"
    fi
}

flattenDir(){
    local DIR="$1"
    find "$DIR" -mindepth 2 -type f -exec mv -i '{}' "$DIR" ';'
}

read -p "Do you wish to flatten folder: ${1}? " -n 1 -r
echo    # (optional) move to a new line
if [[ $REPLY =~ ^[Yy]$ ]]
then
    flattenDir "$1" &
    rmEmptyDirs "$1" &
    echo "Done";
fi
Bruno
sumber
Sobat, saya baru saja menyalahgunakan skrip Anda dengan melupakan argumen path, yang benar-benar mengacaukan server saya. Ok, saya orang yang menyalin hal-hal yang menempel dan menyalahgunakannya, tetapi teman-teman, jadilah bijak dan tambahkan cek / konfirmasi pada skrip yang menghapus / memindahkan barang-barang seperti itu ...
dulgan
Aduh! Saya menyesal mendengar bahwa. Semoga Anda memiliki cadangan ... Saya menambahkan konfirmasi untuk perlindungan di masa depan.
Bruno
Terima kasih @ Bruno, ini jauh lebih baik. Server saya masih berjalan dengan sempurna, saya berkomentar bagian "rata" untuk hanya menghapus direktori kosong secara rekursif dari (dan itu kesalahan saya) root, sampai saya melihat kesalahan yang membuat saya berhenti menjalankan skrip.
dulgan
1

Memperluas jawaban populer untuk pertanyaan ini, karena saya memiliki kasus penggunaan untuk meratakan direktori yang berisi file dengan nama yang sama.

dir1/
├── dir2
   └── file
└── dir3
    └── file

Dalam hal ini, opsi -i( --interactive) yang diteruskan ke mvtidak akan menghasilkan hasil yang diinginkan untuk meratakan struktur direktori dan menangani konflik nama. Jadi itu hanya diganti dengan --backup=t(setara dengan --backup=numbered). Dokumentasi lebih lanjut tentang opsi -b( --backup) tersedia di https://www.gnu.org/software/coreutils/manual/coreutils.html#Backup-options .

Yang menghasilkan:

find dir1/ -mindepth 2 -type f -exec mv -t dir1/ --backup=t '{}' +

Yang menghasilkan:

dir1/
├── dir2
├── dir3
├── file
└── file.~1~
Yann Eves
sumber
1

tar dan zip keduanya memiliki kemampuan untuk menggabungkan dan kemudian menghapus struktur direktori, jadi saya dapat dengan cepat meratakan direktori bersarang dengan

tar -cvf all.tar *

diikuti dengan memindahkan all.tar ke lokasi baru

tar -xvf all.tar --strip=4

John
sumber