Find kullanarak dosyaları taşırken dizin yapısını koru


22

Eski gün dosyalarını kaynak dizinden hedef dizine tanımlandığı şekilde taşıyan aşağıdaki betiği yarattım. Mükemmel çalışıyor.

#!/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"

Bu betiği büyük dosyalar taşır, ayrıca kaynak alt dizinin dosyalarını da taşır, ancak hedef dizine alt dizin oluşturmaz. Bu ek özelliği içinde uygulamak istiyorum.

örnek

/home/ketan     : source directory

/home/ketan/hex : source subdirectory

/home/maxi      : destination directory

Bu betiği çalıştırdığımda, aynı zamanda hex'in dosyalarını maxi dizininde taşıyor, ancak aynı hex'in maxi dizininde yaratılması ve dosyalarının orada aynı hex'de taşınması gerekiyor.

Yanıtlar:


13

Çalıştırmak yerine mv /home/ketan/hex/foo /home/maxi, hedef dizini tarafından üretilen yolu temel alarak değiştirmeniz gerekir find. Önce kaynak dizini değiştirip çalıştırırsanız, bu daha kolaydır find .. Artık yalnızca hedef dizini tarafından üretilen her bir öğeye hazırlayabilirsiniz find. find … -execBirleştirmeyi gerçekleştirmek ve gerekirse hedef dizini oluşturmak için komutta bir kabuk çalıştırmanız gerekir.

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" {} \;

$destinationÖzel karakterler içeriyorsa, alıntılardan kaçınmak için sadece kabuk betiğinin yerine kullanamayacağınızı unutmayın. Onu çevreye verebilirsiniz, böylece iç kabuğa ulaşır ya da bir argüman olarak geçirebilirsiniz (ben de öyle yaptım). shÇağrıları gruplayarak biraz yürütme süresi kazanabilirsiniz :

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" {} +

Alternatif olarak, zsh'de, yalnızca doğru tarih aralığındaki normal dosyaları eşleştirmek için zmvişlevi ve .ve m glob niteleyicileri kullanabilirsiniz. mvGerekirse önce hedef dizini oluşturan alternatif bir işlev iletmeniz gerekir.

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, ;orada bir eksik var :). Ayrıca, ne elde etmek istediğinizi bilmiyorum $0ama bunun olacağına ikna oldum sh:).
Michał Górny

@ MichałGórny for x; doteknik olarak POSIX uyumlu değil ( dilbilgisini kontrol edin ), ancak modern kabukları hem for x dove hem de for x; do; bazı eski Bourne mermileri bozulmadı for x; do. Modern kabukları, günü sh -c '…' arg0 arg1 arg2 arg3, arg0olur $0, arg1olur $1İsterseniz, vb $0olmak sh, sen yazma gerekir sh -c '…' sh arg1 arg2 arg3. Yine, bazı Bourne mermileri farklı şekilde davrandı, ancak POSIX bunu belirtti.
Gilles 'SO- kötülük yapmayı bırak'

pushdcdMevcut çevreye daha az müdahaleci olduğu için daha iyi bir seçim gibi görünüyor .
jpmc26

16

findBelirtildiğini biliyorum , ama bu bir iş gibi geliyor rsync.

Çoğu zaman aşağıdakileri kullanırım:

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

Yalnızca belirli bir dosya türündeki dosyaları taşımak istiyorsanız iyi bir örnek ( örnek ):

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

diğer çözümlerden çok daha temiz :)
Guillaume

2
Bunun --remove-source-filesyararlı olduğunu gördüm , bu da dosyaların kopyalanmak yerine etkin bir şekilde taşınmasına neden olur. Bu, rsync'in harika bir kullanımı.
Tom,

3

İki find (1) örneğini kullanarak yapabilirsiniz.

Her zaman cpio var (1)

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

CPIO'nun argümanlarını kontrol edin. Verdiklerimi


1
Bu, boşluklu tüm dosya isimlerini bozacaktır.
Chris Down

1
Bu, dosyaları taşımak yerine kopyalar.
Gilles 'SO- kötülük yapmayı bırak'

Mükemmel bir cevap olmayabilir, ancak yolları koruyan dosyaları az ya da çok düz bir şekilde hareket ettirmeme yardımcı oldu (dosyaları kopyalayıp silmek için yeterli alana sahibim). Olumlu oyu
AhHatem

3

O kadar verimli değil, ancak kodun okunması ve anlaşılması daha kolay, bence dosyaları kopyalayıp daha sonra silerseniz.

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

Uyarı: Otomatik işlemler için @ MV tarafından keşfedilen kusur:

İki ayrı işlem kullanmak risklidir. Kopyalama işlemi yapılırken bazı dosyalar 7 günden daha eski olursa, kopyalanmayacak ancak silme işlemi ile silinecekler. Bir kez elle yapılan bir şey için bu bir sorun olmayabilir, ancak otomatik komut dosyaları için bu veri kaybına neden olabilir


2
İki ayrı işlem kullanmak risklidir. Kopyalama işlemi yapılırken bazı dosyalar 7 günden daha eski olursa, kopyalanmayacak ancak silme işlemi ile silinecekler. Bir kez elle yapılan bir şey için bu bir sorun olmayabilir, ancak otomatik komut dosyaları için bu veri kaybına neden olabilir.
MV.

2
Bu kusurun kolay çözümü bir kez bulup çalıştırmak, listeyi bir metin dosyası olarak kaydetmek ve ardından kopyalamayı ve sonra silmeyi yapmak için xargs'ı iki kez kullanmaktır.
David M. Perlman,

1

Bunu, findhedef yolunuza döndürülen dosyanın mutlak yolunu ekleyerek yapabilirsiniz :

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

Chris şimdi tüm evini
maxi'ye

@ K.KPatel Hayır, değil. Sadece dizin yapınızı korur.
Chris Down

Bu kırılırsa $destination , özel karakterler içeriyorsa , çünkü iç kabuğun içinde genişlemeye uğrar. Belki demek istedin destination='\'"$destination"\''? Hala kırılıyor '. Ayrıca, /home/maxi/home/ketan/hex/foobunun yerine dosyalar oluşturur /home/maxi/hex/foo.
Gilles 'SO- kötü olmayı'

0

Daha iyi (en hızlı ve taşıma yerine kopya yaparak depolama alanını tüketmeden), adlarında özel karakterler varsa, dosya adlarından da etkilenmez:

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'

Ya da daha hızlı, "paralel" komutunu kullanarak çoklu CPU için aynı anda bir sürü dosyayı hareket ettirin:

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

Not: Bir yazım kaynağınız var, "soure" "kaynak" olmalı. Değişken adını sakladım.


0

Daha az zarif ancak dosyaların sayısı / boyutu çok fazla değilse kolay

Dosyalarınızı birlikte sıkıştırın zip arşive sıkıştırın ve sonra hedefteki -jseçenek olmadan sıkıştırmayı açın . Varsayılan olarak, zip göreceli dizin yapısını yaratacaktır.


0

Bu yolu dene:

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

0

Çünkü bunun için gerçekten kolay bir çözüm yok gibi görünüyor ve çok sık ihtiyacım var, linux için bu açık kaynak kodlu programı yarattım (python gerektirir): https://github.com/benapetr/smv

İhtiyacınız olanı elde etmek için nasıl kullanabileceğinizin birçok yolu vardır, ancak muhtemelen en basit olanı şöyle olabilir:

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

Ayrıca kuru modda çalıştırabilirsiniz, böylece ne yapılacağını yazdırma dışında hiçbir şey yapmaz

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

Veya dosya listesinin argüman satırına sığmayacak kadar uzun olması ve her bir dosya için python çalıştırmayı sakıncası yoksa, o zaman

find "$soure" -type f -mtime "-$days" -exec smv {} "$destination" \;
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.