Pertahankan struktur direktori saat memindahkan file menggunakan find


22

Saya telah membuat skrip berikut yang memindahkan file masa lalu seperti yang didefinisikan dari direktori sumber ke direktori tujuan. Ini bekerja dengan sempurna.

#!/bin/bash

echo "Enter Your Source Directory"
read soure

echo "Enter Your Destination Directory"
read destination 

echo "Enter Days"
read days



 find "$soure" -type f -mtime "-$days" -exec mv {} "$destination" \;

  echo "Files which were $days Days old moved from $soure to $destination"

Script ini memindahkan file dengan sangat baik, juga memindahkan file dari subdirektori sumber, tetapi tidak membuat subdirektori ke direktori tujuan. Saya ingin mengimplementasikan fitur tambahan ini di dalamnya.

dengan contoh

/home/ketan     : source directory

/home/ketan/hex : source subdirectory

/home/maxi      : destination directory

Ketika saya menjalankan skrip ini, ia juga memindahkan file hex di direktori maxi, tetapi saya membutuhkan hex yang sama yang harus dibuat ke direktori maxi dan memindahkan file-file di hex yang sama di sana.

Jawaban:


13

Alih-alih menjalankan mv /home/ketan/hex/foo /home/maxi, Anda harus memvariasikan direktori target berdasarkan jalur yang dihasilkan oleh find. Ini lebih mudah jika Anda mengubah ke direktori sumber terlebih dahulu dan jalankan find .. Sekarang Anda hanya dapat menambahkan direktori tujuan ke setiap item yang diproduksi oleh find. Anda harus menjalankan shell di find … -execperintah untuk melakukan concatenation, dan untuk membuat direktori target jika perlu.

destination=$(cd -- "$destination" && pwd) # make it an absolute path
cd -- "$source" &&
find . -type f -mtime "-$days" -exec sh -c '
  mkdir -p "$0/${1%/*}"
  mv "$1" "$0/$1"
' "$destination" {} \;

Perhatikan bahwa untuk menghindari mengutip masalah jika $destinationmengandung karakter khusus, Anda tidak bisa hanya menggantinya di dalam skrip shell. Anda dapat mengekspornya ke lingkungan sehingga mencapai kulit bagian dalam, atau Anda bisa meneruskannya sebagai argumen (itulah yang saya lakukan). Anda mungkin menghemat sedikit waktu eksekusi dengan mengelompokkan shpanggilan:

destination=$(cd -- "$destination" && pwd) # make it an absolute path
cd -- "$source" &&
find . -type f -mtime "-$days" -exec sh -c '
  for x do
    mkdir -p "$0/${x%/*}"
    mv "$x" "$0/$x"
  done
' "$destination" {} +

Atau, di zsh, Anda dapat menggunakan zmvfungsi , dan kualifikasi. dan m glob untuk mencocokkan file biasa di rentang tanggal yang tepat. Anda harus memberikan alternatifmv fungsi yang pertama kali membuat direktori target jika perlu.

autoload -U zmv
mkdir_mv () {
  mkdir -p -- $3:h
  mv -- $2 $3
}
zmv -Qw -p mkdir_mv $source/'**/*(.m-'$days')' '$destination/$1$2'

for x do, Anda ada yang hilang di ;sana :). Juga, saya tidak tahu apa yang ingin Anda capai $0tetapi saya cukup yakin itu akan menjadi sh:).
Michał Górny

@ MichałGórny for x; dosecara teknis tidak kompatibel dengan POSIX (periksa tata bahasa ), tetapi shell modern memungkinkan keduanya for x dodan for x; do; beberapa cangkang Bourne tua tidak grok for x; do. Pada shell modern, dengan sh -c '…' arg0 arg1 arg2 arg3, arg0menjadi $0, arg1menjadi $1, dll. Jika Anda ingin $0menjadi sh, Anda perlu menulis sh -c '…' sh arg1 arg2 arg3. Sekali lagi, beberapa shell Bourne berperilaku berbeda, tetapi POSIX menentukan ini.
Gilles 'SANGAT berhenti menjadi jahat'

pushdsepertinya pilihan yang lebih baik cd, karena kurang mengganggu ke lingkungan saat ini.
jpmc26

16

Saya tahu findtelah ditentukan, tetapi ini terdengar seperti pekerjaan untuk rsync.

Saya paling sering menggunakan yang berikut:

rsync -axuv --delete-after --progress Source/ Target/

Ini adalah contoh yang bagus jika Anda hanya ingin memindahkan file dari tipe file tertentu ( contoh ):

rsync -rv --include '*/' --include '*.js' --exclude '*' --prune-empty-dirs Source/ Target/

jauh lebih bersih daripada solusi lain :)
Guillaume

2
Saya menemukan itu --remove-source-filessangat membantu, yang secara efektif menyebabkan file dipindahkan daripada disalin. Ini adalah penggunaan rsync yang luar biasa.
Tom

3

Anda bisa melakukannya menggunakan dua instance find (1)

Selalu ada cpio (1)

(cd "$soure" && find  | cpio -pdVmu "$destination")

Periksa argumen untuk cpio. Yang saya berikan


1
Ini akan merusak semua nama file dengan spasi putih.
Chris Down

1
Ini menyalin file alih-alih memindahkannya.
Gilles 'SO- stop being evil'

Mungkin bukan jawaban yang sempurna, tetapi ini membantu saya untuk memindahkan file yang mempertahankan jalur dengan cara yang kurang lebih lurus ke depan (saya memiliki cukup ruang untuk menyalin file kemudian menghapus). Diperbaharui
AhHatem

3

Ini tidak seefisien itu, tetapi kodenya lebih mudah dibaca dan dimengerti, menurut saya, jika Anda hanya menyalin file dan kemudian menghapusnya.

find /original/file/path/* -mtime +7 -exec cp {} /new/file/path/ \;
find /original/file/path/* -mtime +7 -exec rm -rf {} \;

Pemberitahuan: Kesalahan ditemukan oleh @MV untuk operasi otomatis:

Menggunakan dua operasi terpisah berisiko. Jika beberapa file menjadi lebih lama dari 7 hari saat operasi penyalinan selesai, mereka tidak akan disalin tetapi mereka akan dihapus oleh operasi hapus. Untuk sesuatu yang dilakukan secara manual sekali ini mungkin tidak menjadi masalah, tetapi untuk skrip otomatis ini dapat menyebabkan hilangnya data


2
Menggunakan dua operasi terpisah berisiko. Jika beberapa file menjadi lebih lama dari 7 hari saat operasi penyalinan selesai, mereka tidak akan disalin tetapi mereka akan dihapus oleh operasi hapus. Untuk sesuatu yang dilakukan secara manual sekali ini mungkin tidak menjadi masalah, tetapi untuk skrip otomatis ini dapat menyebabkan hilangnya data.
MV.

2
Solusi mudah untuk cacat ini adalah menjalankan find sekali, simpan daftar sebagai file teks, dan kemudian gunakan xargs dua kali untuk melakukan salin dan kemudian hapus.
David M. Perlman

1

Anda dapat melakukan ini dengan menambahkan jalur absolut dari file yang dikembalikan oleh findke jalur tujuan Anda:

find "$soure" -type f -mtime "-$days" -print0 | xargs -0 -I {} sh -c '
    file="{}"
    destination="'"$destination"'"
    mkdir -p "$destination/${file%/*}"
    mv "$file" "$destination/$file"'

Chris sekarang memindahkan seluruh rumah ke maxi
Ketan Patel

@ K.KPatel Tidak, tidak. Itu hanya membuat struktur direktori Anda.
Chris Down

Ini rusak jika $destination berisi karakter khusus, karena mengalami ekspansi di shell bagian dalam. Mungkin maksud Anda destination='\'"$destination"\''? Itu masih pecah '. Juga, ini membuat file seperti /home/maxi/home/ketan/hex/foobukan /home/maxi/hex/foo.
Gilles 'SO- berhenti bersikap jahat'

0

Lebih baik (tercepat & tanpa menghabiskan ruang penyimpanan dengan melakukan penyalinan alih-alih memindahkan), juga tidak terpengaruh oleh nama-nama file jika mereka mengandung karakter khusus dalam namanya:

export destination
find "$soure" -type f "-$days" -print0 | xargs -0 -n 10 bash -c '
for file in "$@"; do
  echo -n "Moving $file to $destination/"`dirname "$file"`" ... "
  mkdir -p "$destination"/`dirname "$file"`
  \mv -f "$file" "$destination"/`dirname "$file"`/ || echo " fail !" && echo "done."
done'

Atau lebih cepat, memindahkan banyak file dalam waktu bersamaan untuk multi-CPU, menggunakan perintah "paralel":

echo "Move oldest $days files from $soure to $destination in parallel (each 10 files by "`parallel --number-of-cores`" jobs):"
function move_files {
  for file in "$@"; do
    echo -n "Moving $file to $destination/"`dirname "$file"`" ... "
    mkdir -p "$destination"/`dirname "$file"`
    \mv -f "$file" "$destination"/`dirname "$file"`/ || echo " fail !" && echo "done."
  done
}
export -f move_files
export destination
find "$soure" -type f "-$days" -print0 | parallel -0 -n 10 move_files

PS: Anda salah ketik, "soure" harus menjadi "sumber". Saya menyimpan nama variabel.


0

Ini kurang elegan tetapi mudah jika jumlah / ukuran file tidak terlalu besar

Zip file Anda bersama menjadi ziparsip, dan kemudian unzip di tujuan tanpa-j opsi. Secara default, zip akan membuat struktur direktori relatif.


0

Coba dengan cara ini:

IFS=$'\n'
for f in `find "$soure" -type f -mtime "-$days"`;
do
  mkdir -p "$destination"/`dirname $f`;
  mv $f "$destination"/`dirname $f`;
done

0

Karena sepertinya tidak ada solusi yang sangat mudah untuk ini dan saya sangat membutuhkannya, saya membuat utilitas open source untuk linux ini (memerlukan python): https://github.com/benapetr/smv

Ada beberapa cara bagaimana Anda dapat menggunakannya untuk mencapai apa yang Anda butuhkan tetapi mungkin yang paling sederhana adalah seperti ini:

 # -vf = verbose + force (doesn't stop on errors)
smv -vf `find some_folder -type f -mtime "-$days"` target_folder

Anda juga dapat menjalankannya dalam mode kering sehingga tidak melakukan apa pun selain mencetak apa yang akan dilakukan

smv -rvf `find some_folder -type f -mtime "-$days"` target_folder

Atau kalau-kalau daftar file terlalu panjang untuk masuk ke dalam baris argumen dan Anda tidak keberatan mengeksekusi python untuk setiap file, maka

find "$soure" -type f -mtime "-$days" -exec smv {} "$destination" \;
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.