Linux'ta 2 dizin ağacını kopyalamadan birleştirme?


35

Benzer düzenlere sahip iki dizin ağacım var, yani

.
 |-- dir1
 |   |-- a
 |   |   |-- file1.txt
 |   |   `-- file2.txt
 |   |-- b
 |   |   `-- file3.txt
 |   `-- c
 |       `-- file4.txt
 `-- dir2
     |-- a
     |   |-- file5.txt
     |   `-- file6.txt
     |-- b
     |   |-- file7.txt
     |   `-- file8.txt
     `-- c
         |-- file10.txt
         `-- file9.txt

Dizinleri ve dir2 dizin ağaçları oluşturmak için birleştirmek istiyorum:

 merged/
 |-- a
 |   |-- file1.txt
 |   |-- file2.txt
 |   |-- file5.txt
 |   `-- file6.txt
 |-- b
 |   |-- file3.txt
 |   |-- file7.txt
 |   `-- file8.txt
 `-- c
     |-- file10.txt
     |-- file4.txt
     `-- file9.txt

Bunu "cp" komutunu kullanarak yapabileceğimi biliyorum, ancak birleştirmek istediğim gerçek dizinler gerçekten büyük ve çok sayıda dosya (milyonlarca) içerdiğinden, dosyaları kopyalamak yerine taşımak istiyorum. Eğer "mv" kullanırsam, çakışan dizin isimleri nedeniyle "Dosya var" hatasını alıyorum.

GÜNCELLEME: İki dizin ağacı arasında çift dosya olmadığını varsayabilirsiniz.


İki klasör arasında dosya adlarının çoğaltılması olmadığından emin misiniz? İki kopya varsa ne olmak istersiniz?
Zoredache,

Kelimenin tam anlamıyla tek bir dizinde milyonlarca dosya varsa, o zaman performansla ilgili nedenlerden dolayı dosyaları ayrı alt dizinlere ayırmaya çalışmalısınız - bu sorulan soru ile ilgisi olmasa da.
DrStalker

Yanıtlar:


28
rsync -ax --link-dest=dir1/ dir1/ merged/
rsync -ax --link-dest=dir2/ dir2/ merged/

Bu oldukça taşımanın dışında sabit bağlarının yaratacak, onlar doğru, o zaman, kaldır taşındı doğrulayabilirsiniz dir1/ve dir2/.


9
Biraz. Aslında herhangi bir disk kullanımını çoğaltmaz, aynı disk parçası için başka bir işaretçi yaratır ve aslında herhangi bir veriyi 'kopyalamaz'. (Bkz. En.wikipedia.org/wiki/Hard_links ) Ancak, bu işlemi dosya başına bir kez yapmak zorundadır. Fakat esasen bu cevapların tamamının yaptığı şey budur, çünkü yalnızca bir dizini taşıyamazsınız.
Christopher Karel,

1
Dosyaları kopyalamanın ek yükü bulunmadığından, bu mükemmel bir şekilde kabul edilebilir bir çözümdür.
Tobu,

2
Bu, ancak aynı dosya sisteminde olsalar da işe yarar. Aynı dosya sisteminde olsaydı, sil seçeneğiyle rsync bir hamle yapar mıydı? (demek ki, sadece dizin bilgisini değiştiriniz, fakat dosyayı taşımayınız).
Ronald Pottol

1
rsync kopyalanacak ve dosya sistemlerini geçerse silecektir.
karmawhore

5
Bir uyarı: --link-destyolu mutlak veya göreceli yapın merged/; ya da kopyalayacak.
Tobu,

21

It garip kimse kaydetti cpseçeneği vardır -l:

-l, --link
       kopyalamak yerine hard link dosyaları

Gibi bir şey yapabilirsin

% mkdir birleştirme
% cp -rl dir1 / * dir2 / * birleştirme
% rm -r dir *
% ağaç birleştirme 
birleştirme
├── a
│ ├── dosya1.txt
│ ├── dosya2.txt
5 ├── dosya5.txt
│ └── dosya6.txt
├── b
│ ├── dosya3.txt
├── ├── dosya7.txt
│ └── dosya8.txt
└── c
    10 dosya10.txt
    4 dosya4.txt
    9 dosya9.txt

13 dizin, 0 dosya

Bu, farklı sabit disklerde çalışmaz ...
Alex Leach

4
Dosya sistemleri birden fazla sabit sürücüye yayılabildiğinden, dosya sistemlerinde çalışmadığını söylemek daha doğrudur. Ayrıca, eğer op isterse dosyaları kopyalamaktan kaçınmaksa, dosya cp -lsistemlerinde çalışmayan iyi bir şeydir .
lvella

2
Dosyaların tüm özniteliklerini saklamak ve cp -aeş anlamlı cp -RPpizlemekten kaçınmak için kullanmak (eş anlamlı ) kullanmak isteyebilirsiniz : burada komut olur cp -al dir1/* dir2/* merge.
Tricasse,

5

Bunun için rename (aka prename, perl paketinden) kullanabilirsiniz. Adın mutlaka debian / ubuntu dışında tanımladığım komuta atıfta bulunmadığına dikkat edin (gerekirse tek bir taşınabilir perl dosyası olmasına rağmen).

mv -T dir1 merged
rename 's:^dir2/:merged/:' dir2/* dir2/*/*
find dir2 -maxdepth 1 -type d -empty -delete

Ayrıca vidir (moreutils'den) kullanma ve dosya yollarını tercih ettiğiniz metin düzenleyiciden düzenleme seçeneğiniz de vardır.


3

Gibi rsync ve prename çözümleri, ama sen gerçekten yapmak istiyorsanız mv işi ve

  • senin bulmak bilir -print0ve -depth,
  • senin Xargs bilir -0,
  • Eğer var printf ,

o zaman adlarında, bir Bourne tarzı kabuk betiği ile rastgele boşluk bırakabilecek çok sayıda dosyayı kullanmak mümkündür:

#!/bin/sh

die() {
    printf '%s: %s\n' "${0##*/}" "$*"
    exit 127
}
maybe=''
maybe() {
    if test -z "$maybe"; then
        "$@"
    else
        printf '%s\n' "$*"
    fi
}

case "$1" in
    -h|--help)
        printf "usage: %s [-n] merge-dir src-dir [src-dir [...]]\n" "${0##*/}"
        printf "\n    Merge the <src-dir> trees into <merge-dir>.\n"
        exit 127
    ;;
    -n|--dry-run)
        maybe=NotRightNow,Thanks.; shift
    ;;
esac

test "$#" -lt 2 && die 'not enough arguments'

mergeDir="$1"; shift

if ! test -e "$mergeDir"; then
    maybe mv "$1" "$mergeDir"
    shift
else
    if ! test -d "$mergeDir"; then
        die "not a directory: $mergeDir"
    fi
fi

xtrace=''
case "$-" in *x*) xtrace=yes; esac
for srcDir; do
    (cd "$srcDir" && find . -print0) |
    xargs -0 sh -c '

        maybe() {
            if test -z "$maybe"; then
                "$@"
            else
                printf "%s\n" "$*"
            fi
        }
        xtrace="$1"; shift
        maybe="$1"; shift
        mergeDir="$1"; shift
        srcDir="$1"; shift
        test -n "$xtrace" && set -x

        for entry; do
            if test -d "$srcDir/$entry"; then
                maybe false >/dev/null && continue
                test -d "$mergeDir/$entry" || mkdir -p "$mergeDir/$entry"
                continue
            else
                maybe mv "$srcDir/$entry" "$mergeDir/$entry"
            fi
        done

    ' - "$xtrace" "$maybe" "$mergeDir" "$srcDir"
    maybe false >/dev/null ||
    find "$srcDir" -depth -type d -print0 | xargs -0 rmdir
done

Xargs'a girişini newline'a çevirmesini ve çeviriyi atlamasını söyleyebilirsiniz. örneğin, aşağıdakiler torrent dosyalarınızı geçerli dizinde, unicode karakterli veya başka bir tomfoolery'de bile bulabilir ve silebilir. find . -name '*.torrent' | xargs -d '\n' rm
PRS

2

Kaba kuvvet bash

#! /bin/bash

for f in $(find dir2 -type f)
do
  old=$(dirname $f)
  new=dir1${old##dir2}
  [ -e $new ] || mkdir $new
  mv $f $new
done

bunu test ediyor

# setup 
for d in dir1/{a,b,c} dir2/{a,b,c,d} ; do mkdir -p $d ;done
touch dir1/a/file{1,2} dir1/b/file{3,4} dir2/a/file{5,6} dir2/b/file{7,8} dir2/c/file{9,10} dir2/d/file11

# do it and look
$ find dir{1,2} -type f
dir1/a/file1
dir1/a/file2
dir1/a/file5
dir1/a/file6
dir1/b/file3
dir1/b/file7
dir1/b/file8
dir1/c/file4
dir1/c/file9
dir1/c/file10
dir1/d/file11

2
OP, bu yapıyı bozması muhtemel milyonlarca dosya belirtti. Ayrıca, boşluk, satırsonu vb. İle dosya adlarını düzgün işlemeyecektir.
Chris Johnsen

0

Kaynak kod ağaçları için farklı gelişim aşamalarında bunu birkaç kez yapmak zorunda kaldım. Benim çözümüm Git'i şu şekilde kullanmaktı:

  1. Bir git deposu oluşturun ve tüm dosyaları dir1'den ekleyin.
  2. işlemek
  3. Tüm dosyaları kaldırın ve dosyalara dir2'den kopyalayın
  4. işlemek
  5. İki önemli nokta arasındaki farkları görün ve sonuçları nasıl birleştirmek istediğim konusunda dikkatli kararlar alın.

Dallanma ve benzeri şeylerle uğraşabilirsiniz ancak genel fikir budur. Ve doldurma konusunda daha az korkunuz var çünkü her bir durumun tam bir enstantanesi var.

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.