Klasörleri mv ile birleştirmek mi istiyorsunuz?


149

mv"Klasör" adlı bir klasörü zaten "klasör" içeren bir dizine taşımak için kullanırsam birleştirecekler mi yoksa değiştirilecekler mi?

Yanıtlar:


120

mvdizinleri birleştiremez veya üzerine yazamazsa , --forceseçeneği kullanırken bile "mv: 'a' '' b ': Dizin boş değil' iletisini alamazsınız .


Bunun için başka araçlar (örneğin rsync, findhatta cp) bile kullanabilirsiniz , ancak sonuçları dikkatlice düşünmelisiniz:

  • rsyncBir dizinin içeriğini bir başkasına birleştirebilir (ideal olarak yalnızca başarılı bir şekilde aktarılan kaynak dosyaları güvenle silme seçeneği ile --remove-source-files1 seçeneğinde ve isterseniz normal izin / sahiplik / zaman koruma seçeneğiyle -a)
    ancak bu tam bir kopyalama işlemidir ve bu nedenle çok disk yoğun olabilir.
  • Şu anda tercih edilen seçenek: birleştirebilirsiniz rsync'ın --link-dest=DIRve (yerine mümkün kopyalama dosya içeriklerin sabit bağları oluşturmak için) seçeneğini --remove-source-filesdüzenli bir semantik çok benzer olsun mv.
    Bunun için, --link-destmutlak bir yol verilmesi gereken kaynak dizin (veya bir göreli yol hedef için kaynak ).
    Ama bu --link-destistenmeyen bir şekilde (komplikasyonlara neden olabilir veya etmeyebilir) kullanıyor, kaynağa giden mutlak yolu (argüman olarak --link-dest) bilmeyi (veya belirlemeyi) gerektiriyor ve tekrar gibi boş bir dizin yapısını bırakıyor 1 başına .
  • findKaynak dizin yapısını hedefteki sırayla yeniden oluşturmak için kullanabilirsiniz , sonra asıl dosyaları ayrı ayrı hareket ettirin
    ancak bunun kaynak üzerinden birden çok kez tekrarlanması ve yarış koşullarıyla karşılaşması gerekir (çok adımlı işlem sırasında kaynakta oluşturulan yeni dizinler) )
  • cpSert bağlantılar oluşturabilirsiniz kaynaşmasında çok benzer bir sonuç yaratır, (basitçe aynı varolan dosyaya ek işaretçileri koymak) mv(ve yalnızca işaretçiler oluşturulan ve hiçbir gerçek veri kopyalanacak vardır çünkü çok IO-verimli)
    ... ama bu yine olası bir yarış koşulundan muzdarip (önceki adımda kopyalanmamış olsalar bile kaynaktaki yeni dosyalar siliniyor)

Bu geçici çözümlerden hangisinin (varsa) uygun olduğu özel kullanım durumunuza büyük ölçüde bağlı olacaktır.
Her zamanki gibi, bu komutlardan herhangi birini yerine getirmeden önce düşünün ve yedekleriniz olsun.


1: rsync --remove-source-filesDizinleri silmeyeceğinize dikkat edin, bu nedenle find -depth -type d -empty -deleteboş kaynak dizin ağacından kurtulmak için daha sonra yapmanız gerekenleri yapın .


Tek bir uygulamasını denemiş gibisiniz mv. Bu cevap daha geniş bir gerçeklikte daha iyi olurdu. Linux, BSD ve "real" Unix veya POSIX veya SUS'tan referans.
Warren Young,

@WarrenYoung Haklısın, ben sadece denedim mvDebian tarafından kullanılan uygulama - vurgu olmak çalıştı manpage bu davranışı söz etmez çünkü ...
n.st

38
Rsync'in dezavantajı, çok fazla veri ile uğraşıyorsanız potansiyel olarak kaynak yoğun olan sabit bağlantıyı değiştirmek yerine verileri gerçekten kopyalamasıdır.
Jonathan Mayer

7
@Keith --deleteYalnızca kaynak dizinde bulunmayan hedef dizindeki dosyaları siler .
n.st

1
@JonathanMayer rsync gibi birkaç hard link ile ilgili özellikler. Örneğin, -Hişlevle yalnızca sabit bağlantıları koruyabilir veya hedef kullanarak dosyaları sabit bağlama yapabilirsiniz --link-dest. Yine de kullanmadan önce man sayfasına bakınız.
allo

87
rsync -av /source/ /destination/
(after checking)
rm -rf /source/

Bu, yorumdaki gibi n.st kaynak dosyalarını kaldıracak mı?
Dominique,

3
Hayır, güvenlik nedeniyle iki adımda yapmayı tercih ederim. Birleştirilmiş ve kaldırılmış kaynak geri alınamaz. N.st anwer içinde ek adım (ayrıca dizinleri kaldırmak için) gereklidir.
fazie

3
--remove-source-filesyalnızca başarılı bir şekilde aktarılan dosyaları kaldırma avantajına sahiptir, bu nedenle findboş dizinleri kaldırmak için kullanabilirsiniz ve rsyncs çıkışını kontrol etmek zorunda kalmadan aktarılmayan her şeyle birlikte kalacaksınız .
n.st

4
Ama aslında hareket etmiyor - eğer büyük dosyalar varsa hız etkisi çok büyük.
Alex

Ama aslında saf hareket ve birleştirme gerçekleştiremezsiniz.
fazie

64

Tam veri kopyaları yerine aynı dosya sisteminde sabit dosya bağlantıları oluşturan cp komutunun -lseçeneğini kullanabilirsiniz . Aşağıdaki komut, klasörü zaten adı olan bir dizin içeren bir üst klasöre ( ) kopyalar .source/folderdestinationfolder

cp -rl source/folder destination
rm -r source/folder

Ayrıca kullanmak isteyebilirsiniz -P( --no-dereference- de-referans sembolik linkler yoktur) veya -a( --archive- tüm meta korumak, aynı zamanda içerir -Pseçeneği), ihtiyaçlarınıza bağlı.


7
@rautamiekka: Sabit bağlantılar kullanma nedenini sorduğunuzu varsayıyorum. Sabit bağlantıların ne olduğunu ve neden kullanmanız gerektiğini bilmiyorsanız, muhtemelen bu rotayı izlememelisiniz. Ancak, sabit bağlantılar oluşturmak tam bir kopya yapmaz, bu nedenle bu işlem tam bir kopyadan daha az zaman alır. Ayrıca, yumuşak bağlantıları kullanmak yerine sabit bağlantıları kullanırsınız; böylece kaynak dosyaları silebilir ve işaretçiler yerine geçersiz yollara doğru verileri alabilirsiniz. Ve her sistemin sahip olduğundan ve herkesin buna aşina cpolduğundan rsyncberi cp.
palswim

7
Bu çözümün parlaklığı kabul edilen cevap olmadığı için gözden geçirilebilir. Bu zarif bir çözümdür. cpİşlem süresi ile birleştirme yeteneği olsun mv.
Herk,

2
-n
Hedefte

1
@Ruslan: Bu doğru, ancak herhangi bir yöntemle dosya sistemlerinde bir kopya olmadan hareket edemezsiniz. Hatta mv /fs1/file /fs2/(dosya sistemlerinde) bir kopya ve ardından bir silme gerçekleştirir.
palswim

2
Doğru, ancak mv“verimli bir şekilde” olmasın veya ne diyorsanız çalışsanız bile (hedef direktör henüz mevcut değilse) cp -rlçalışacaktır.
Ruslan

23

Bu dört adımı tavsiye ederim:

cd ${SOURCE}; 
find . -type d -exec mkdir -p ${DEST}/\{} \; 
find . -type f -exec mv \{} ${DEST}/\{} \; 
find . -type d -empty -delete

veya daha iyisi, işte benzer bir anlambilim uygulayan bir betik mv:

#!/bin/bash

DEST="${@:${#@}}"
ABS_DEST="$(cd "$(dirname "$DEST")"; pwd)/$(basename "$DEST")"

for SRC in ${@:1:$((${#@} -1))}; do   (
    cd "$SRC";
    find . -type d -exec mkdir -p "${ABS_DEST}"/\{} \;
    find . -type f -exec mv \{} "${ABS_DEST}"/\{} \;
    find . -type d -empty -delete
) done

Args KAYNAK, DEST vardır
schuess

Bu oldukça kullanışlı görünüyor. Sabit diskimi temizlemek için kullanmaya başlıyorum. Başka bir uzman bu betiğe bir sürü yedekleme emanet etmeden önce bu konuda yorum yapabilir mi? :-)
LarsH

BTW, eşdeğerini yapmak istiyorsanız rsync -u(yalnızca yeniyse güncelle), mv(en azından bazı sürümlerde) de bu -useçeneği kullanabilir. Ancak bu durumda, kaynak ağacındaki dosyaların daha yeni olmadığı durumlarda, boş olmayan kaynak dizinleri ve boş olanları silmek isteyebilirsiniz. @schuess: Gerekirse birden fazla SOURCE argümanı olabilir gibi görünüyor.
LarsH

1
Bu, boşlukları iyi idare etmiyor. İçinde boşluk bulunan bazı dizinleri denedim ve sonsuz sayıda iç içe dizin dizisine son verdim.
rofer

16

İşte dizinleri birleştirecek bir yol. Dosyaları kopyalamak ve silmek yerine yalnızca yeniden adlandırdığından, rsync'den çok daha hızlıdır.

cd source; find -type f -print0 | xargs -0 -n 1 -I {} mv '{}' 'dest/{}'

Bu ilginç ama sadece belli bir konuyla alakalı ve kullanıcının ne istediğini bile uzaktan göremiyor.
Shadur

17
Aslında, Jewel kodu tam olarak kullanıcının istediği şeyi yapar, eksik dizinler oluşturmak dışında. Belki de tekrar bakmalısın?
Jonathan Mayer

3
İsimlerde "-print0" ve xargs'ta "-0" kullanmak ekleyecektim çünkü adlarında boşluk olan dosyalar var. Ayrıca, bir ad parantez içeriyorsa taşınmayacakları küçük bir sorun vardır.
markuz

2
Bu, az sayıda dosya için rsync'den çok daha hızlıdır, ancak her dosya için yeni bir işlem gerektirir, bu nedenle performans, çok sayıda küçük dosya ile doludur. @ Palswim'in cevabı bu sorundan muzdarip değil.
b0fh

2
destZaten içinde ile aynı ada sahip bir dizinse, komut başarısız olur source. Ve dosyalar taşınır destiçinde olan source. Komut hiçbir şey yapmazmv source/* source/dest/.
ceving

3

Bunu başarmanın bir yolu kullanmak olacaktır:

mv folder/* directory/folder/
rmdir folder

Sürece İki dosya aynı ada sahip olarak folderve directory/folder, sen birleştirme yani aynı sonucu elde edecektir.


3
Tam olarak nasıl rm folderçalışıyor?
JakeGould

5
@JakeGould Hiç de değil. :)
n.st

rm folder -fRher zaman benim için çalışır
Octopus

2
Bunun gizli dosyalar için işe yaramayacağına dikkat edin
b0fh

2

En saf kopyalar için, tar (-) B blockread copy yöntemini kullanıyorum.

örneğin, kaynak yolunun içinden (gerekirse orada 'cd' var):

tar cBf - <sourcefolder> | (cd /your/target/folder ; tar xBf -)

bu kaynak ağacın tam bir kopyasını yaratır, sahibiyle ve izinleri bozulmadan. Hedef klasör varsa, veriler birleştirilir. Sadece önceden var olan dosyaların üzerine yazılacaktır.

Örnek:

 $ cd /data1/home
 $ tar cBf - jdoe | (cd /data2/home ; tar xBf -)

Kopyalama işlemi başarılı olduğunda kaynağı ( rm -rf <source>) kaldırabilirsiniz . Tabii ki bu kesin bir hamle değil: Siz kaynağı kaldırana kadar veriler kopyalanacaktır.

Seçenek olarak, -v ile ayrıntılı olabilir (ekranda kopyalanan dosyayı görüntüleyin). tar cBvf -

  • c: yaratmak, yapmak, tasarlamak, üretmek
  • B: tam bloğu oku (boru okuması için)
  • v: ayrıntılı
  • f: yazmak için dosya
  • x: Ayıkla
  • -: stdout / stdin

sourcefolderolabilir *(geçerli klasördeki herhangi bir şey için)


Tar'ı belirtmek f -genellikle gereksizdir - varsayılan, stdin / write'dan stdout'a okumaktır.
muru

1

İşte benim için çalışan bir senaryo. Rsync yerine mv'yi tercih ediyorum, bu yüzden Jewel ve Jonathan Mayer'in çözümlerini kullanıyorum.

#!/bin/bash

# usage source1 .. sourceN dest

length=$(($#-1))
sources=${@:1:$length}
DEST=$(readlink -f ${!#})
for SRC in $sources; do
    pushd $SRC;
    find . -type d -exec mkdir -p ${DEST}/{} \;
    find . -type f -exec mv {} ${DEST}/{} \;
    find . -type d -empty -delete
    popd
done

Bu çözüm yol adlarından tam olarak çıkmıyor, dikkatli olun.
user12439

@ user12439, hangi parçanın düzeltileceğini gösterirseniz, çözümü güncelleyeceğim.
xer0x

1

Cp veya rsync gibi komutları kullanmak iyi bir fikir değildir. Büyük dosyalar için uzun zaman alacaktır. mv dosyaları fiziksel olarak kopyalamaksızın sadece inode'ları güncellediğinden çok daha hızlıdır. İşletim sisteminizin dosya yöneticisini kullanmak daha iyi bir seçenek olabilir. Opensuse için Konquerer adında bir dosya yöneticisi var. Dosyaları gerçekten kopyalamaksızın taşıyabilir. Windows'taki gibi "kes ve yapıştır" işlevine sahiptir. A dizinindeki tüm alt dizinleri seçmeniz yeterlidir, sağ tıklayın ve aynı isimlere sahip alt dizinler içerebilen B dizinine "hareket ettirin". Onları birleştirecek. Aynı addaki dosyaların üzerine yazmak veya yeniden adlandırmak isteyip istemediğinizi de seçebilirsiniz.


1
OP mvkullanıldığında ne olacağını soruyor .
don_crissti

0

Python çözümü

Önceden tatmin edici bir çözüm bulamadığım için hızlı bir Python senaryosu hazırlamaya karar verdim.

Özellikle, bu yöntem etkilidir çünkü kaynak dosya ağacını sadece bir kere yukarı doğru yürür.

Ayrıca, dosya üzerine yazma gibi şeyleri istediğiniz gibi hızla değiştirebilmenizi sağlar.

Kullanımı:

move-merge-dirs src/ dest/

tüm içeriği src/*içine taşıyacak dest/ve src/kaybolacak.

move-birleştirme-dirs

#!/usr/bin/env python3

import argparse
import os

def move_merge_dirs(source_root, dest_root):
    for path, dirs, files in os.walk(source_root, topdown=False):
        dest_dir = os.path.join(
            dest_root,
            os.path.relpath(path, source_root)
        )
        if not os.path.exists(dest_dir):
            os.makedirs(dest_dir)
        for filename in files:
            os.rename(
                os.path.join(path, filename),
                os.path.join(dest_dir, filename)
            )
        for dirname in dirs:
            os.rmdir(os.path.join(path, dirname))
    os.rmdir(source_root)

if __name__ == '__main__':
    parser = argparse.ArgumentParser(
        description='Move merge src/* into dest. Overwrite existing files.'
    )
    parser.add_argument('src_dir')
    parser.add_argument('dest_dir')
    args = parser.parse_args()
    move_merge_dirs(args.src_dir, args.dest_dir)

GitHub yukarı akış .

Ayrıca bakınız: https://stackoverflow.com/questions/22588225/how-do-you-merge-two-directories-or-move-with-replace-from-the-windows-command


0

Bu, dosyaları ve klasörleri başka bir hedefe taşımak için kullanılan komuttur:

$ mv /source/path/folder /target/destination/

Unutmayın : mvKlasör b̲e̲i̲n̲g m̲e̲r̲ge̲d̲ (yani, aynı adda başka bir klasör hedefte zaten var ise) ve d̲e̲s̲t̲i̲n̲a̲t̲i̲o̲n̲ o̲n̲e̲ i̲s̲ n̲o̲t̲ e̲m̲p̲p̲y̲t .

mv: '/ source / path / folder' '/ target / destination / folder' konumuna getirilemiyor: Dizin boş değil

Hedef klasör boşsa, yukarıdaki komut iyi çalışacaktır.

Yani, her iki klasörü de birleştirmek için,
iki komutla da yapın:

$ cp -rf /source/path/folder /target/destination/
$ rm -rf /source/path/folder

Veya her ikisini de tek seferlik bir komut olarak birleştirin:

$ cp -rf /source/path/folder /target/destination/ && rm -rf /source/path/folder

mv = hareket
cp = kopya
rm = kaldır

-r dizini (klasörü)
-f zorla yürütme

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.