लिनक्स में 2 निर्देशिका पेड़ों को कॉपी किए बिना मर्ज करें?


35

मेरे पास समान लेआउट वाले दो निर्देशिका पेड़ हैं, अर्थात

.
 |-- 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

मैं बनाने के लिए dir1 और dir2 निर्देशिका पेड़ों का विलय करना चाहूंगा:

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

मुझे पता है कि मैं "cp" कमांड का उपयोग करके ऐसा कर सकता हूं, लेकिन मैं नकल के बजाय फाइलों को स्थानांतरित करना चाहता हूं, क्योंकि जिन वास्तविक निर्देशिकाओं को मैं विलय करना चाहता हूं, वे वास्तव में बड़ी हैं और बहुत सारी फाइलें (लाखों) हैं। यदि मैं "mv" का उपयोग करता हूं तो मुझे परस्पर विरोधी निर्देशिका नामों के कारण "फ़ाइल मौजूद है" त्रुटि मिलती है।

अद्यतन: आप मान सकते हैं कि दो निर्देशिका पेड़ों के बीच कोई डुप्लिकेट फ़ाइलें नहीं हैं।


क्या आप सुनिश्चित हैं कि दो फ़ोल्डरों के बीच फ़ाइल नाम का कोई दोहराव नहीं है? यदि आप डुप्लिकेट हैं तो आप क्या करना चाहते हैं?
Zoredache

यदि आपके पास वास्तव में एक ही निर्देशिका में लाखों फाइलें हैं, तो आपको प्रदर्शन कारणों से अलग-अलग उप निर्देशिकाओं में फ़ाइलों को विभाजित करने पर ध्यान देना चाहिए - हालांकि यह पूछे गए वास्तविक प्रश्न के लिए अप्रासंगिक है।
DrStalker

जवाबों:


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

यह उन्हें स्थानांतरित करने के बजाय हार्डलिंक बनाएगा, आप यह सत्यापित कर सकते हैं कि वे सही ढंग से चले गए थे, फिर, निकालें dir1/और dir2/


9
एक प्रकार का। यह वास्तव में किसी भी डिस्क उपयोग की नकल नहीं करता है, यह बस डिस्क के एक ही हंटर को एक और पॉइंटर बनाता है, और वास्तव में किसी भी डेटा को 'कॉपी' नहीं करता है। ( En.wikipedia.org/wiki/Hard_links देखें ) हालांकि, यह उस ऑपरेशन को प्रति फ़ाइल एक बार करना होगा। लेकिन यह अनिवार्य रूप से है कि ये सभी उत्तर क्या कर रहे हैं, क्योंकि आप केवल एक निर्देशिका को स्थानांतरित नहीं कर सकते हैं।
क्रिस्टोफर कारेल

1
चूंकि इसमें फ़ाइलों की प्रतिलिपि बनाने का io ओवरहेड नहीं है, इसलिए यह पूरी तरह स्वीकार्य समाधान है।
टोबू

2
यह केवल तभी काम करता है जब वे एक ही फाइल सिस्टम पर हों। यदि वे एक ही फाइल सिस्टम पर थे, तो डिलीट ऑप्शन के साथ rsync एक मूव करेगा? (यह कहना है, बस निर्देशिका जानकारी बदलें, लेकिन फ़ाइल को स्थानांतरित न करें)।
रोनाल्ड पोटोल

1
rsync कॉपी करेगा, फिर हटाएगा यदि यह फ़ाइल सिस्टम को ट्रैवर्स करता है।
कर्मवहोर

5
एक चेतावनी: --link-destपथ को निरपेक्ष बनाएं , या उसके सापेक्ष merged/; या यह कॉपी करेगा।
तोबू

21

यह अजीब है कि किसी के cpपास विकल्प नहीं है -l:

-एल, -लिंक
       हार्ड कॉपी फ़ाइलों के बजाय नकल

आप कुछ ऐसा कर सकते हैं

% मकदिर मर्ज
% cp -rl dir1 / * dir2 / * मर्ज करें
% rm -r dir *
% पेड़ विलीन हो गए 
मर्ज
├── ए
1 │ file1.txt
2 │ file2.txt
5 ├── file5.txt
6 └── file6.txt
├── बी
3 ├── file3.txt
7 │ file7.txt
8 │ file8.txt
└── सी
    Xt file10.txt
    Xt file4.txt
    Xt file9.txt

13 निर्देशिका, 0 फाइलें

यह अलग-अलग हार्ड ड्राइव पर काम नहीं करता है ...
एलेक्स लीच

4
यह कहना अधिक सही है कि यह फाइलसिस्टम में काम नहीं करता है, क्योंकि फाइलसिस्टम कई हार्ड ड्राइव में फैला हो सकता है। इसके अलावा, अगर ओप चाहता है कि फाइलों को कॉपी करने से बचें, तो यह अच्छी बात है कि cp -lफाइलसिस्टम में काम नहीं करता है।
लवेला

2
आप फ़ाइलों के सभी गुणों को रखने के cp -aलिए (समानार्थक शब्द cp -RPp) का उपयोग करना चाह सकते हैं और सहानुभूति का पालन करने से बच सकते हैं: यहां कमांड बन जाता है cp -al dir1/* dir2/* merge
त्रिकसेन

5

आप उस के लिए नाम बदलें (उर्फ prename, पर्ल पैकेज से) का उपयोग कर सकते हैं। सावधान रहें कि नाम जरूरी नहीं कि मैं उस आदेश का संदर्भ देता हूं जिसे मैं डेबियन / ubuntu से बाहर का वर्णन करता हूं (हालांकि यदि आपको इसकी आवश्यकता है तो यह एक एकल पोर्टेबल पर्ल फ़ाइल है)।

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

आपके पास vidir (moreutils से) का उपयोग करने और अपने पसंदीदा पाठ संपादक से फ़ाइल पथ संपादित करने का विकल्प भी है।


3

मैं rsync और prename समाधान पसंद करता हूं , लेकिन अगर आप वास्तव में mv काम करना चाहते हैं और

  • आपका पता है -print0और -depth,
  • आपके xargs को पता है -0,
  • आपके पास प्रिंटफ है ,

फिर बड़ी संख्या में उन फाइलों को संभालना संभव है जिनके नाम में बेतरतीब व्हाट्सएप हो सकता है, सभी एक बॉर्न-शैली शेल स्क्रिप्ट के साथ:

#!/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 को बता सकते हैं। उदाहरण के लिए वर्तमान निर्देशिका के अंतर्गत आपकी सभी टोरेंट फाइल्स को ढूंढना और हटाना, यहां तक ​​कि यूनिकोड वर्णों या कुछ अन्य टोमफूलरी के साथ भी। find . -name '*.torrent' | xargs -d '\n' rm
पीआरएस

2

पाशविक बल 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

परीक्षण यह करता है

# 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
ओपी ने लाखों फाइलें निर्दिष्ट कीं, जो इस निर्माण को तोड़ने की संभावना है। इसके अलावा, यह ठीक से रिक्त स्थान, newlines, आदि के साथ filenames को संभाल नहीं होगा ..
क्रिस जॉन्सन

0

मुझे विकास के विभिन्न चरणों में स्रोत कोड पेड़ों के लिए ऐसा कई बार करना पड़ा है। मेरा समाधान निम्नलिखित तरीके से Git का उपयोग करना था:

  1. एक git भंडार बनाएँ और dir1 से सभी फ़ाइलों को जोड़ें।
  2. कमिट
  3. सभी फ़ाइलों को निकालें और dir2 से फ़ाइलों में प्रतिलिपि बनाएँ
  4. कमिट
  5. दो प्रतिबद्ध बिंदुओं के बीच अंतर देखें और परिणामों को मर्ज करने के तरीके के बारे में सावधानीपूर्वक निर्णय लें।

आप इसे ब्रांचिंग और इसके साथ आगे बढ़ा सकते हैं लेकिन यह सामान्य विचार है। और आपको इसे भरवाने के बारे में कम डर है क्योंकि आपके पास प्रत्येक राज्य का पूरा स्नैपशॉट है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.