UNIX shell scripting: bagaimana cara memindahkan file secara rekursif ke satu direktori?

8

Saya memiliki sejumlah besar file kecil, f, disusun dalam struktur direktori seperti:

/A/B/C/f

Ada 11 direktori di level A, masing-masing dengan sekitar 100 direktori di level B, masing-masing dengan sekitar 30 direktori di level C, masing-masing dengan satu file f.

Bagaimana cara memindahkan semua file untuk satu tingkat? Misalnya, mengingat kumpulan file ini ...

/ A / B / C / f1
/ A / B / C / f2
/ A / B / C / f3
/ A / B / C / f4

Saya ingin direktori /A/B/berisi 4 file, f1 hingga f4. Menghapus direktori C tidak perlu.

Saya berharap ini adalah masalah diselesaikan, mungkin melibatkan find, xargsdan whatnot. Ada ide?

Bersulang,

James

jl6
sumber

Jawaban:

9

Ini cukup sederhana dengan temuan GNU (seperti yang ditemukan di Linux) atau temuan lain yang mendukung -execdir:

find A -type f -execdir mv -i {} .. \;

Dengan standar find:

find A -type f -exec sh -c 'mv -i "$1" "${1%/*}/.."' sh {} \;

Dengan zsh:

zmv -Q -o-i 'A/(**/)*/(*)(.)' 'A/$1$2'

Jika struktur direktori selalu memiliki tingkat bersarang yang sama, Anda tidak perlu traversal rekursif (tetapi hapus direktori kosong terlebih dahulu):

for x in */*; do; echo mv -i "$x"/*/* "$x"/..; done
Gilles 'SANGAT berhenti menjadi jahat'
sumber
1

Untuk kumpulan file itu, ini bisa dilakukan:

$ cd /A/B/C/
$ mv ./* ../

Tapi saya berharap masalah Anda agak lebih rumit ... Saya tidak bisa menjawabnya ... Saya tidak yakin bagaimana struktur dir Anda ... Bisakah Anda mengklarifikasi?

Stack Overflow sudah mati
sumber
Ini jelas akan bekerja untuk satu direktori / A / B / C, tetapi karena saya memiliki sekitar 30000 direktori seperti itu, saya berharap untuk perintah yang dapat dieksekusi di bagian atas hierarki untuk mengurus semuanya sekaligus.
jl6
1
@ jl6 Begitu, dan hanya file yang perlu dipindahkan? Jadi C tidak harus pindah ke B, dan B tidak ke A, dll ...
Stack Overflow sudah mati
Itu benar - operasi hanya boleh dilakukan pada file, pada level terendah hirarki, dan mereka hanya harus dipindahkan satu level ke atas.
jl6
0

Dugaan pertama saya adalah

$ find A -type f -exec mv {} .. \;  

selama Anda tidak menentukan -depthitu harus ok. Saya belum mencobanya jadi ujilah terlebih dahulu sebelum Anda berkomitmen untuk itu.

hotei
sumber
Apa yang ditemukan ketika ada dua file dengan nama yang sama?
Temukan bukan masalahnya, perintah "mv" tertanam. Nama dupe tidak akan bagus karena mv tidak akan mengganti nama, itu akan ditimpa. Anda bisa menggunakan mv -i untuk membantu sedikit, tetapi dengan 30.000+ file yang mungkin menyakitkan. Jika Anda perlu memiliki banyak kendali atas situasi tersebut, Anda mungkin memerlukan program yang ditulis dalam bahasa scripting (pilihan saya adalah python, YMMV).
hotei
Saya belum pernah menggunakan "backup" dengan mv, tetapi melihat halaman manual sepertinya itu bisa menjadi solusi untuk masalah Anda nama dupe. "temukan A -type f -exec mv - mundur bernomor {} .. \;" mungkin bekerja (perhatikan itu tanda hubung ganda di - backup)
hotei
Contoh find yang diberikan akan memindahkan semua file pada level terendah ke direktori di atas A (saya percaya ".." ditafsirkan oleh shell sebelum diteruskan untuk menemukan / mv?). Saya perlu file untuk naik satu tingkat ke atas dalam hierarki. Satu klarifikasi lebih lanjut: tidak akan ada nama file duplikat, asalkan file hanya bergerak SATU tingkat atas.
jl6
@hotei: mvPerintah dijalankan di direktori saat ini, jadi ..jangan lakukan apa yang Anda harapkan. Lihat jawaban saya untuk solusi. @ jl6: shell tidak ada hubungannya .., itu ditangani oleh kernel.
Gilles 'SO- stop being evil'
0

Jika Anda hanya ingin memindahkan file yang ada di direktori daun (yaitu Anda tidak ingin pindah /A/B/fileke /Ajika Bberisi subdirektori) maka berikut adalah beberapa cara untuk melakukannya:

Keduanya membutuhkan ini

leaf ()
{
    find $1 -depth -type d | sed 'h; :b; $b; N; /^\(.*\)\/.*\n\1$/ { g; bb }; $ {x; b}; P; D'
}
shopt -s nullglob

Yang ini berfungsi:

leaf A | while read -r dir
do
    for file in "$dir"/*
    do
        parent=${dir%/*}
        if [[ -e "$parent/${file##*/}" ]]
        then
            echo "not moved: $file"
        else
            mv "$file" "$parent"
        fi
    done
done

Ini akan lebih cepat, tetapi tidak suka direktori sumber kosong:

leaf A | while read -r dir
do
    mv -n "${dir}"/* "${dir%/*}"
    remains=$(echo "$dir"/*)
    [[ -n "$remains" ]] && echo "not moved: $remains"
done
Dijeda sampai pemberitahuan lebih lanjut.
sumber