निर्देशिका संरचना को सिंक करने का कोई तरीका जब फाइल पहले से ही दोनों तरफ हैं?


24

मेरे पास एक ही फाइल के साथ दो ड्राइव हैं, लेकिन निर्देशिका संरचना बिल्कुल अलग है।

क्या गंतव्य पक्ष की सभी फ़ाइलों को 'स्थानांतरित' करने का कोई तरीका है ताकि वे स्रोत पक्ष की संरचना से मेल खा सकें? एक स्क्रिप्ट के साथ शायद?

उदाहरण के लिए, ड्राइव A में है:

/foo/bar/123.txt
/foo/bar/234.txt
/foo/bar/dir/567.txt

जबकि ड्राइव B में है:

/some/other/path/123.txt
/bar/doo2/wow/234.txt
/bar/doo/567.txt

विचाराधीन फाइलें बहुत बड़ी हैं (800GB), इसलिए मैं उन्हें फिर से कॉपी नहीं करना चाहता; मैं केवल आवश्यक निर्देशिका बनाकर और फ़ाइलों को स्थानांतरित करके संरचना को सिंक करना चाहता हूं।

मैं एक पुनरावर्ती स्क्रिप्ट के बारे में सोच रहा था जो गंतव्य पर प्रत्येक स्रोत फ़ाइल को ढूंढेगा, फिर इसे एक मिलान निर्देशिका में ले जाएगा, यदि आवश्यक हो तो बना सकता है। लेकिन - यह मेरी क्षमताओं से परे है!

एक और सुरुचिपूर्ण समाधान यहां दिया गया था: /superuser/237387/any-way-to-sync-directory-structure-when-the-files-are-already-on-both-sides-238086


क्या आप सुनिश्चित हैं कि नाम विशिष्ट रूप से किसी फ़ाइल की सामग्री को निर्धारित करता है, अन्यथा आपको उनके चेकसमों द्वारा फाइलों की तुलना करने पर विचार करना चाहिए।
kasterma

जवाबों:


11

मैं गाइल्स के साथ जाऊंगा और आपको हिसन जे द्वारा सुझाए गए अनुसार यूनिसन की ओर इशारा करूंगा । ड्रॉपबॉक्स से 20 साल पहले यूनिसन ड्रॉपबॉक्स था। रॉक सॉलिड कोड जिसे बहुत सारे लोग (खुद शामिल) हर दिन इस्तेमाल करते हैं - सीखने के लिए बहुत ही सार्थक। फिर भी, joinसभी प्रचार की जरूरत है इसे प्राप्त कर सकते हैं :)


यह केवल आधा उत्तर है, लेकिन मुझे काम पर वापस जाना है :)

मूल रूप से, मैं अल्पज्ञात joinउपयोगिता को प्रदर्शित करना चाहता था जो कि बस कुछ क्षेत्रों में दो तालिकाओं में मिलती है।

सबसे पहले, रिक्त स्थान के साथ फ़ाइल नाम सहित एक परीक्षण मामला सेट करें:

for d in a b 'c c'; do mkdir -p "old/$d"; echo $RANDOM > "old/${d}/${d}.txt"; done
cp -r old new

(कुछ निर्देशिका और / या फ़ाइल नाम संपादित करें new)।

अब, हम एक नक्शा बनाना चाहते हैं: हैश -> प्रत्येक निर्देशिका के लिए फ़ाइल नाम और फिर joinउसी हैश के साथ फ़ाइलों का मिलान करने के लिए उपयोग करें। नक्शा बनाने के लिए, निम्नलिखित में से एक में डाल दिया makemap.sh:

find "$1" -type f -exec md5 -r "{}" \; \
  | sed "s/\([a-z0-9]*\) ${1}\/\(.*\)/\1 \"\2\"/" \

makemap.sh फ़ॉर्म की पंक्तियों के साथ एक फ़ाइल को बाहर निकालता है, 'हैश "फ़ाइलनाम"', इसलिए हम सिर्फ पहले कॉलम में शामिल होते हैं:

join <(./makemap.sh 'old') <(./makemap.sh 'new') >moves.txt

यह उत्पन्न करता है moves.txtजो इस तरह दिखता है:

49787681dd7fcc685372784915855431 "a/a.txt" "bar/a.txt"
bfdaa3e91029d31610739d552ede0c26 "c c/c c.txt" "c c/c c.txt"

अगला कदम वास्तव में चालें चलना होगा, लेकिन मेरे प्रयास उद्धृत करने पर अटक गए ... mv -iऔर mkdir -pकाम करना चाहिए।


क्षमा करें, मुझे इसकी कोई समझ नहीं है!
दान

1
joinवास्तव में दिलचस्प है। इसे मेरे संज्ञान में लाने के लिए धन्यवाद।
स्टीवन डी

@Dan। माफ़ कीजिये। समस्या यह है कि मुझे नहीं पता कि मैं आपके फ़ाइल नामों के बारे में क्या अनुमान लगा सकता हूं। मान्यताओं के बिना स्क्रिप्टिंग कोई मज़ा नहीं है, विशेष रूप से इस मामले में जहां मैंने फ़ाइल नामों को एक फ़ाइल dwheeler.com/essays/fixing-unix-linux-filenames.html पर आउटपुट करने के लिए चुना ।
Janus

1
यह शायद बहुत समय (और सीपीयू लोड) बर्बाद करता है क्योंकि एमडी 5 हैश बनाने के लिए इन विशाल फाइलों को पूरी तरह से पढ़ना होगा। यदि फ़ाइल का नाम और फ़ाइल का आकार मेल खाता है, तो संभवत: फ़ाइलों को हैश करने के लिए ओवरकिल है। हाशिंग एक दूसरे चरण में किया जाना चाहिए और सिर्फ उन फाइलों के लिए जो नाम या आकार में कम से कम एक (एक ही डिस्क पर) से मेल खाती हैं।
हॉके लैगिंग

क्या आपको joinइनपुट के रूप में उपयोग की जाने वाली फ़ाइलों को क्रमबद्ध करने की आवश्यकता नहीं है ?
cjm

8

एक उपयोगिता है जिसे यूनिसन कहा जाता है:

http://www.cis.upenn.edu/~bcpierce/unison/

साइट से विवरण:

Unison, Unix और Windows के लिए एक फाइल-सिंक्रोनाइज़ेशन टूल है। यह अलग-अलग होस्ट (या एक ही होस्ट पर अलग-अलग डिस्क) पर संग्रहीत फ़ाइलों और निर्देशिकाओं के संग्रह के दो प्रतिकृतियों को अलग से संशोधित करने की अनुमति देता है, और फिर प्रत्येक प्रतिकृति में बदलावों को दूसरे के लिए प्रचारित करके आज तक लाया जाता है।

ध्यान दें कि यदि केवल कम से कम एक रूट रिमोट है, तो Unison केवल पहले रन की गई फ़ाइलों का पता लगाता है, इसलिए भले ही आप फ़ाइलों को सिंक्रोनाइज़ कर रहे हों, उपयोग करें ssh://localhost/path/to/dir जड़ों में से एक के रूप में ।


@ गिल्स: क्या आपको यकीन है? मैं सब कुछ के लिए एकसमान का उपयोग करता हूं और अक्सर इसे उन फ़ाइलों को खोलकर देखता हूं जिनका नाम बदल दिया गया है और / या बहुत दूर चले गए हैं। क्या आप यह कह रहे हैं कि यह केवल पहले से ही सिंक की गई फ़ाइलों के लिए काम करता है, जहाँ पर इनसाइड नंबर (या जो भी अन्य ट्रिक्स इसका उपयोग करता है) को रिकॉर्ड करने का मौका मिला है?
Janus

@ जानुस: सुधार के लिए धन्यवाद, मेरी टिप्पणी वास्तव में गलत थी। यूनिसन उन फ़ाइलों का पता लगाता है, जिन्हें प्रारंभिक रन पर भी स्थानांतरित किया गया था। (यह तब नहीं होता है जब दोनों जड़ें स्थानीय होती हैं, यही कारण है कि यह मेरे परीक्षण में ऐसा नहीं किया।) इसलिए एक बहुत अच्छा सुझाव है।
गिल्स एसओ- बुराई को रोकना '

@Gilles। पता करने के लिए अच्छा है - वहाँ काफी कुछ स्थानों जहाँ एल्गोरिथ्म स्थानीय और दूरदराज के syncs के बीच अलग है लगता है। मैंने वास्तव में नहीं सोचा था कि यह पहले सिंक के लिए काम करेगा। ए 1 के लिए!
Janus

4

उपयोग यूनिसन के रूप में hasen j ने सुझाव दिया । मैं इस उत्तर को संभावित उपयोगी स्क्रिप्टिंग उदाहरण के रूप में या केवल स्थापित मूल उपयोगिताओं वाले सर्वर पर उपयोग के लिए छोड़ रहा हूं।


मुझे लगता है कि फ़ाइल नाम पूरे पदानुक्रम में अद्वितीय हैं। मैं यह भी मानूंगा कि किसी फ़ाइल नाम में एक नई पंक्ति नहीं है, और यह कि निर्देशिका पेड़ों में केवल निर्देशिकाएं और नियमित फाइलें होती हैं।

  1. सबसे पहले स्रोत के नाम पर फ़ाइल नाम एकत्र करें।

    (cd /A && find . \! -type d) >A.find
  2. उसके बाद गंतव्य स्थान पर फ़ाइलों को स्थानांतरित करें। सबसे पहले, गंतव्य की तरफ फ़ाइलों का एक चपटा पेड़ बनाएं। यदि आप पुराने पदानुक्रम में हार्ड लिंक रखना चाहते हैं ln, mvतो इसके बजाय का उपयोग करें ।

    mkdir /B.staging /B.new
    find /B.old -type f -exec sh -c 'mv -- "$@" "$0"' /B.staging {} +
  3. यदि गंतव्य में कुछ फाइलें गायब हो सकती हैं, तो समान रूप से चपटा /A.stagingबनाएं और डेटा को स्रोत से गंतव्य तक कॉपी करने के लिए rsync का उपयोग करें।

    rsync -au /A.staging/ /B.staging/
  4. अब फ़ाइलों का नाम बदलें।

    cd /B.new &&
    <A.find perl -l -ne '
      my $dir = '.'; s!^\./+!!;
      while (s!^([^/]+)/+!!) {  # Create directories as needed
        $dir .= "/$1";
        -d $dir or mkdir $dir or die "mkdir $dir: $!"
      }
      rename "/B.staging/$_", "$dir/$_" or die "rename -> $dir/$_: $!"
    '

    इसके तुल्य:

    cd /B.new &&
    <A.find python -c '
    import os, sys
    for path in sys.stdin.read().splitlines():
        dir, base = path.rsplit("/", 2)
        os.rename(os.path.join("/B.new", base), path)
    '
  5. अंत में, यदि आप निर्देशिकाओं के मेटाडेटा की परवाह करते हैं, तो पहले से मौजूद फाइलों के साथ rsync को कॉल करें।

    rsync -au /A/ /B.new/

ध्यान दें कि मैंने इस पोस्ट में स्निपेट्स का परीक्षण नहीं किया है। अपने जोखिम पार इस्तेमाल करें। कृपया किसी टिप्पणी में किसी त्रुटि की रिपोर्ट करें।


2

विशेष रूप से अगर ऑन-गोइंग सिंक उपयोगी होगा, तो आप गिट-एनेक्स का पता लगाने की कोशिश कर सकते हैं

यह अपेक्षाकृत नया है; मैंने खुद इसका इस्तेमाल करने की कोशिश नहीं की है।

मैं इसे सुझाने में सक्षम हूं क्योंकि यह फाइलों की एक दूसरी प्रति रखने से बचता है ... इसका मतलब यह है कि इसे फाइलों को केवल पढ़ने के लिए ("लॉक") के रूप में चिह्नित करना है, जैसे कुछ गैर-गिट संस्करण संस्करण नियंत्रण प्रणाली।

फ़ाइलें sha256sum + फ़ाइल एक्सटेंशन (डिफ़ॉल्ट रूप से) द्वारा पहचानी जाती हैं। तो यह समान फ़ाइल सामग्री के साथ दो रेपो को सिंक करने में सक्षम होना चाहिए, लेकिन अलग-अलग फ़ाइलनाम, बिना लिखने के लिए (और यदि वांछित हो तो कम बैंडविड्थ नेटवर्क पर)। यह निश्चित रूप से उन्हें चेकसम करने के लिए सभी फाइलों को पढ़ना होगा।


1

इस जैसे किसी और के बारे में क्या राय है:

src=/mnt/driveA
dst=/mnt/driveB

cd $src
find . -name <PATTERN> -type f >/tmp/srclist
cd $dst
find . -name <PATTERN> -type f >/tmp/dstlist

cat /tmp/srclist | while read srcpath; do
    name=`basename "$srcpath"`
    srcdir=`dirname "$srcpath"`
    dstpath=`grep "/${name}\$" /tmp/dstlist`

    mkdir -p "$srcdir"
    cd "$srcdir" && ln -s "$dstpath" "$name"
done

यह मानता है कि जिन फ़ाइलों को आप सिंक करना चाहते हैं, उनके नाम पूरे ड्राइव में अद्वितीय हैं: अन्यथा इसका कोई तरीका नहीं है कि इसे पूरी तरह से स्वचालित किया जा सके (हालांकि, आप उपयोगकर्ता के लिए एक संकेत प्रदान कर सकते हैं कि कौन सी फ़ाइल चुनने के लिए यदि वहाँ अधिक है।)

ऊपर दी गई स्क्रिप्ट साधारण मामलों में काम करेगी, लेकिन अगर nameरिग्क्स के लिए विशेष अर्थ रखने वाले प्रतीकों को शामिल करने में विफल रहता है । grepअगर वहाँ फ़ाइलों का बहुत कुछ है फ़ाइलों की सूची पर भी बहुत समय लग सकता है। आप हैशटेबल का उपयोग करने के लिए इस कोड का अनुवाद करने पर विचार कर सकते हैं जो फ़ाइल नाम को रास्तों में ले जाएगा, जैसे कि रूबी में।


यह आशाजनक लग रहा है - लेकिन क्या यह फ़ाइलों को स्थानांतरित करता है, या सिर्फ सीमलिंक बनाता है?
दान

मुझे लगता है कि मैं इसे सबसे अधिक समझता हूं; लेकिन grepरेखा क्या करती है? क्या यह सिर्फ मेल खाने वाली फ़ाइल का पूरा रास्ता ढूंढता है dstlist?
दान

@ दान: जाहिरा तौर पर इसके उपयोग से सहानुभूति lnपैदा होती है। आप mvफ़ाइलों को स्थानांतरित करने के लिए नियोजित कर सकते हैं , लेकिन मौजूदा लोगों को अधिलेखित करने से सावधान रहें। इसके अलावा, आप फ़ाइलों को दूर ले जाने के बाद, यदि कोई हो, तो खाली dirs को साफ करना चाहते हैं। हां, वह grepकमांड एक लाइन को खोजता है जो फाइलनाम पर समाप्त होता है, इस प्रकार गंतव्य ड्राइव पर इसका पूरा रास्ता बताता है।
एलेक्स

1

आधार फ़ाइलनामों को पेड़ों में अद्वितीय मानते हुए, यह बिल्कुल सीधा है:

join <(cd A; find . -type f | while read f; do echo $(basename $f) $(dirname $f); done | sort) \
     <(cd B; find . -type f | while read f; do echo $(basename $f) $(dirname $f); done | sort) |\
while read name to from
do
        mkdir -p B/$to
        mv -v B/$from/$name B/$to/
done

यदि आप पुरानी खाली निर्देशिकाओं को साफ करना चाहते हैं, तो उपयोग करें:

find B -depth -type d -delete

1

मुझे भी इस समस्या का सामना करना पड़ा। Md5sum -based समाधान मेरे लिए काम नहीं किया, क्योंकि मैं अपनी फ़ाइलों को सिंक करता हूंwebdav माउंट । कम्प्यूटिंग md5sum पर sumswebdavगंतव्य मतलब बड़े फ़ाइल संचालन भी होगा।

मैंने एक छोटी स्क्रिप्ट बनाई reorg_Remote_Dir_detect_moves.sh (जीथब पर) जो सबसे अधिक पता लगाने की कोशिश कर रही है स्थानांतरित फ़ाइलों और फिर दूरस्थ निर्देशिका को समायोजित करने के लिए कई कमांडों के साथ एक नई अस्थायी शेल-स्क्रिप्ट बनाता है। चूंकि मैं केवल फ़ाइल नामों का ध्यान रखता हूं, इसलिए स्क्रिप्ट कोई सही समाधान नहीं है।

सुरक्षा के लिए, कई फ़ाइलों को अनदेखा किया जाएगा: ए) हर तरफ (समान शुरुआत) नामों वाली फाइलें, और बी) फाइलें जो केवल दूरस्थ तरफ हैं। उन्हें नजरअंदाज कर छोड़ दिया जाएगा।

छोड़ी गई फाइलें तब आपके पसंदीदा सिंक टूल (जैसे) द्वारा नियंत्रित की जाएंगी rsync, unison , ...) , जिसे आपको अस्थायी शेल-स्क्रिप्ट चलाने के बाद उपयोग करना होगा।

तो शायद मेरी स्क्रिप्ट किसी के लिए उपयोगी है? यदि ऐसा है (इसे और अधिक स्पष्ट करने के लिए) तीन चरण हैं:

  1. शेल स्क्रिप्ट चलाएं reorg_Remote_Dir_detect_moves.sh (जीथब पर)
  2. यह अस्थायी शेल-स्क्रिप्ट बनाएगा /dev/shm/REORGRemoteMoveScript.sh= चालें करने के लिए इसे चलाएं (घुड़सवार पर तेज होगा webdav)
  3. अपना पसंदीदा समन्वयन उपकरण चलाएं (जैसे rsync, unison, ...)

1

यहाँ एक उत्तर में मेरा प्रयास है। एक forewarning के रूप में, मेरे सभी स्क्रिप्टिंग अनुभव बैश से आते हैं, इसलिए यदि आप एक अलग शेल का उपयोग कर रहे हैं, तो कमांड के नाम या सिंटैक्स अलग हो सकते हैं।

इस समाधान के लिए दो अलग स्क्रिप्ट बनाने की आवश्यकता होती है।

यह पहला स्क्रिप्ट वास्तव में गंतव्य ड्राइव पर फ़ाइलों को स्थानांतरित करने के लिए जिम्मेदार है।

md5_map_file="<absolute-path-to-a-temporary-file>"

# Given a single line from the md5 map file, list
# only the path from that line.
get_file()
{
  echo $2
}

# Given an md5, list the filename from the md5 map file
get_file_from_md5()
{
  # Grab the line from the md5 map file that has the
  # md5 sum passed in and call get_file() with that line.
  get_file `cat $md5_map_file | grep $1`
}

file=$1

# Compute the md5
sum=`md5sum $file`

# Get the new path for the file
new_file=`get_file_from_md5 $sum`

# Make sure the destination directory exists
mkdir -p `dirname $new_file`
# Move the file, prompting if the move would cause an overwrite
mv -i $file $new_file

दूसरी स्क्रिप्ट पहले स्क्रिप्ट द्वारा उपयोग किए जाने वाले md5 मैप फ़ाइल को बनाती है और फिर गंतव्य ड्राइव में प्रत्येक फ़ाइल पर पहली स्क्रिप्ट को कॉल करती है।

# Do not put trailing /
src="<absolute-path-to-source-drive>"
dst="<absolute-path-to-destination-drive>"
script_path="<absolute-path-to-the-first-script>"
md5_map_file="<same-absolute-path-from-first-script>"


# This command searches through the source drive
# looking for files.  For every file it finds,
# it computes the md5sum and writes the md5 sum and
# the path to the found filename to the filename stored
# in $md5_map_file.
# The end result is a file listing the md5 of every file
# on the source drive
cd $src
find . -type f -exec md5sum "{}" \; > $md5_map_file

# This command searches the destination drive for files and calls the first
# script for every file it finds.
cd $dst
find . -type f -exec $script_path '{}' \; 

असल में, जो चल रहा है वह दो लिपियों के साथ एक साहचर्य सारणी का अनुकरण है $md5_map_file । सबसे पहले, स्रोत ड्राइव पर फ़ाइलों के लिए सभी md5s को गणना और संग्रहीत किया जाता है। Md5s के साथ संबद्ध ड्राइव की जड़ से संबंधित पथ हैं। फिर, गंतव्य ड्राइव पर प्रत्येक फ़ाइल के लिए, md5 की गणना की जाती है। इस md5 का उपयोग करते हुए, स्रोत ड्राइव पर उस फ़ाइल का पथ दिखाई देता है। गंतव्य ड्राइव पर फ़ाइल को तब स्रोत ड्राइव पर फ़ाइल के पथ से मिलान करने के लिए ले जाया जाता है।

इस लिपि के साथ कुछ दोहे हैं:

  • यह मानता है कि $ dst में प्रत्येक फ़ाइल $ src में भी है
  • यह $ dst से किसी भी निर्देशिका को नहीं हटाता है, केवल फाइलों को स्थानांतरित करता है। मैं वर्तमान में स्वचालित रूप से ऐसा करने का सुरक्षित तरीका सोचने में असमर्थ हूं

Md5 की गणना करने के लिए एक लंबा समय लेना चाहिए: सभी सामग्री को वास्तव में पढ़ा जाना चाहिए। यदि डैन सुनिश्चित करता है कि फाइलें समान हैं, तो बस उन्हें निर्देशिका संरचना में स्थानांतरित करना बहुत तेज है (कोई पढ़ना नहीं)। तो, md5sumलगता है कि यहाँ इस्तेमाल करने वाली चीज़ नहीं है। (BTW, rsyncएक मोड है जिसमें यह चेकसमों की गणना नहीं करता है।)
imz - Ivan Zakharyaschev

यह सटीकता और गति के बीच एक व्यापार है। मैं एक ऐसी विधि प्रदान करना चाहता था जिसमें केवल फ़ाइल नाम की तुलना में अधिक सटीकता का उपयोग किया जाता था।
क्लेडूक्स
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.