किसी फ़ाइल को ट्रांसेक्शनल रूप से कॉपी कैसे करें?


9

मैं ए से बी तक एक फाइल कॉपी करना चाहता हूं, जो विभिन्न फाइल सिस्टम पर हो सकती है।

कुछ अतिरिक्त आवश्यकताएं हैं:

  1. प्रतिलिपि सभी या कुछ भी नहीं है, दुर्घटना में कोई भी आंशिक या भ्रष्ट फ़ाइल बी नहीं बचा है;
  2. मौजूदा फ़ाइल B को अधिलेखित न करें;
  3. एक ही आदेश के समवर्ती निष्पादन के साथ प्रतिस्पर्धा न करें, कम से कम एक सफल हो सकता है।

मुझे लगता है कि यह करीब हो जाता है:

cp A B.part && \
ln B B.part && \
rm B.part

लेकिन 3. बीपी मौजूद नहीं होने पर cp द्वारा उल्लंघन किया जाता है (यहां तक ​​कि -n फ्लैग के साथ भी)। इसके बाद 1. असफल हो सकता है अगर अन्य प्रक्रिया cp को जीतती है और फ़ाइल को जगह में लिंक करना अधूरा है। B.part एक असंबंधित फ़ाइल भी हो सकती है, लेकिन मुझे उस मामले में अन्य छिपे हुए नामों की कोशिश किए बिना असफल होने की खुशी है।

मुझे लगता है कि बैश नोकलेबर मदद करता है, क्या यह पूरी तरह से काम करता है? क्या बैश संस्करण आवश्यकता के बिना प्राप्त करने का एक तरीका है?

#!/usr/bin/env bash
set -o noclobber
cat A > B.part && \
ln B.part B && \
rm B.part

फॉलोअप, मुझे पता है कि कुछ फ़ाइल सिस्टम इस (एनएफएस) पर वैसे भी विफल होंगे। क्या ऐसे फाइल सिस्टम का पता लगाने का कोई तरीका है?

कुछ अन्य संबंधित लेकिन समान प्रश्न नहीं:

लगभग फाइल सिस्टम में परमाणु चाल?

क्या मेरी fs पर mv परमाणु है?

वहाँ अस्थायी रूप से फ़ाइल और निर्देशिका को अस्थायी रूप से eMMC पर ext4 विभाजन में ले जाने का एक तरीका है

https://rcrowley.org/2010/01/06/things-unix-can-do-atomically.html


2
क्या आप केवल एक ही कमांड के समवर्ती निष्पादन के बारे में चिंतित हैं (जैसे कि आपके उपकरण के भीतर पर्याप्त लॉक हो सकता है), या फ़ाइलों के साथ अन्य बाहरी हस्तक्षेप के बारे में भी?
माइकल होमर

3
"Transactional" बेहतर हो सकता है
muru

1
उपकरण के भीतर @MichaelHomer काफी अच्छा है, मुझे लगता है कि चीजें बहुत कठिन होंगी! अगर इसकी फ़ाइल ताले के साथ संभव है ...
इवान बेने

1
@marcelm mvएक मौजूदा फ़ाइल को अधिलेखित कर देगा। बी mv -nयह सूचित नहीं करेगा कि यह विफल हो गया है। ln(1)( rename(2)) विफल हो जाएगा यदि बी पहले से मौजूद है।
इवान बेने

1
@EvanBenn अच्छा बिंदु! मुझे आपकी आवश्यकताओं को बेहतर ढंग से पढ़ना चाहिए। (मुझे एक मौजूदा लक्ष्य के परमाणु अपडेट की आवश्यकता है, और मैं उस बात को ध्यान में रखते हुए जवाब दे रहा था)
मार्च २०'२०

जवाबों:


11

rsyncयह काम करता है एक अस्थायी फ़ाइल O_EXCLडिफ़ॉल्ट रूप से बनाई जाती है (केवल यदि आप उपयोग करते हैं तो अक्षम --inplace) और फिर renamedलक्ष्य फ़ाइल पर। --ignore-existingयदि यह मौजूद है तो बी को अधिलेखित न करने के लिए उपयोग करें ।

व्यवहार में, मैंने इसके साथ ext4, zfs या यहां तक ​​कि NFS माउंट पर किसी भी समस्या का अनुभव नहीं किया।


rsync शायद यह अच्छी तरह से करता है, लेकिन अत्यंत जटिल मैन पेज मुझे डराता है। अन्य विकल्पों को लागू करने वाले विकल्प, एक दूसरे के साथ असंगत होना आदि
इवान बेने

रुप्रिंक # 3 आवश्यकता के साथ मदद नहीं करता है, जहां तक ​​मैं बता सकता हूं। फिर भी, यह एक शानदार उपकरण है, और आपको थोड़े से मानव-पृष्ठ पढ़ने से शर्म नहीं करनी चाहिए। आप या तो github.com/tldr-pages/tldr/blob/master/pages/common/rsync.md या ch.sh/rsync की कोशिश कर सकते हैं । (tldr और धोखा दो अलग अलग परियोजनाओं है कि समस्या आप कहा गया है, अर्थात् के साथ मदद करने का लक्ष्य कर रहे हैं, "आदमी पेज टीएल है, डॉ", आम आदेशों के बहुत सारे का समर्थन कर रहे हैं, और आप सबसे आम उपयोगों के रूप में दिखाई
सीताराम

@EvanBenn rsync एक अद्भुत उपकरण है और अच्छी तरह से सीखने लायक है! यह मैन पेज जटिल है क्योंकि यह इतना बहुमुखी है। डरा मत बनो :)
जोश

@sitaram, # 3 को पिड फ़ाइल के साथ हल किया जा सकता है। उत्तर में एक छोटी स्क्रिप्ट जैसी ।
रॉबर्ट रिडेल

2
यह सबसे अच्छा जवाब है। Rsync परमाणु फ़ाइल स्थानान्तरण के लिए उद्योग मानक गो-इन है, और विभिन्न कॉन्फ़िगरेशन में आपकी सभी आवश्यकताओं को पूरा कर सकते हैं।
18

4

चिंता मत करो, noclobberएक मानक विशेषता है


धन्यवाद, इस रसीले उत्तर को स्वीकार करने का प्रलोभन। NFS की तरह डोडी फाइलसिस्टम पर कोई टिप्पणी?
इवान बेने

@ EvanBenn, मेरा मतलब था कि अगर NFS आपको किसी तरह से यहाँ गड़बड़ करने जा रहा है तो मुझे यकीन नहीं है, लेकिन मैं भूल गया।
ilkachachu

4

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

वहाँ वास्तव में एक सामान्य जाँच करने के लिए नहीं है कि फाइलसिस्टम जो आप लिख रहे हैं वह noclobberपरमाणु रूप से कुछ का समर्थन करेगा या नहीं। आप फाइल सिस्टम प्रकार की जांच कर सकते हैं, चाहे वह एनएफएस हो, लेकिन यह एक अनुमानी होगा और जरूरी नहीं कि गारंटी हो। SMB / CIFS (सांबा) जैसे फाइलसिस्टम के समान समस्याओं से ग्रस्त होने की संभावना है। फ़ाइल सिस्टम FUSE के माध्यम से उजागर होता है या सही ढंग से व्यवहार नहीं कर सकता है, लेकिन यह ज्यादातर कार्यान्वयन पर निर्भर करता है।


संभवतः एक बेहतर दृष्टिकोण B.partएक अद्वितीय फ़ाइल नाम (अन्य एजेंटों के साथ सहयोग के माध्यम से) के चरण में टकराव से बचने के लिए है , ताकि आपको इस पर निर्भर होने की आवश्यकता न हो noclobber। उदाहरण के लिए, आप फ़ाइल नाम, अपने होस्टनाम, पीआईडी ​​और टाइमस्टैम्प (+ एक यादृच्छिक संख्या।) के भाग के रूप में शामिल कर सकते हैं, क्योंकि किसी भी समय एक मेजबान में एक विशिष्ट पीआईडी ​​के तहत चलने वाली एक ही प्रक्रिया होनी चाहिए, यह होना चाहिए। विशिष्टता की गारंटी।

तो या तो एक:

test -f B && continue  # skip already existing
unique=$(hostname).$$.$(date +%s).$RANDOM
cp A B.part."$unique"
# Maybe check for existance of B again, remove
# the temporary file and bail out in that case.
mv B.part."$unique" B
# mv (rename) should always succeed, overwrite a
# previously copied B if one exists.

या:

test -f B && continue  # skip already existing
unique=$(hostname).$$.$(date +%s).$RANDOM
cp A B.part."$unique"
if ln B.part."$unique" B ; then
    echo "Success creating B"
else
    echo "Failed creating B, already existed"
fi
# Both cases require cleanup.
rm B.part."$unique"

इसलिए यदि आपके पास दो एजेंटों के बीच एक दौड़ की स्थिति है, तो वे दोनों ऑपरेशन के साथ आगे बढ़ेंगे, लेकिन अंतिम ऑपरेशन परमाणु होगा, इसलिए या तो बी ए की पूरी प्रति के साथ मौजूद है, या बी मौजूद नहीं है।

आप प्रतिलिपि के बाद mvया lnऑपरेशन से पहले फिर से जाँच करके दौड़ के आकार को कम कर सकते हैं , लेकिन वहाँ अभी भी एक छोटी दौड़ की स्थिति है। लेकिन, दौड़ की स्थिति की परवाह किए बिना, बी की सामग्री सुसंगत होनी चाहिए, यह मानते हुए कि दोनों प्रक्रियाएं इसे ए (या मूल के रूप में एक मान्य फ़ाइल से एक प्रति) बनाने की कोशिश कर रही हैं।

ध्यान दें कि पहली स्थिति में mv, जब कोई दौड़ मौजूद होती है, तो अंतिम प्रक्रिया वही होती है जो जीतता है, क्योंकि नाम बदलकर (2) किसी मौजूदा फ़ाइल को बदल देगा।

यदि newpath पहले से मौजूद है, तो इसे परमाणु रूप से बदल दिया जाएगा, ताकि ऐसा कोई बिंदु न हो जिस पर newpath तक पहुंचने का प्रयास करने वाली अन्य प्रक्रिया उसे अनुपलब्ध लगेगी । [...]

यदि newpath मौजूद है, लेकिन ऑपरेशन किसी कारण से विफल हो जाता है, तो newpathrename() का एक उदाहरण छोड़ने की गारंटी देता है।

तो, इस समय B का उपभोग करने वाली काफी संभव प्रक्रियाएं इस प्रक्रिया के दौरान इसके विभिन्न संस्करणों (अलग-अलग इनोड्स) को देख सकती हैं। यदि लेखक सिर्फ एक ही सामग्री को कॉपी करने की कोशिश कर रहे हैं, और पाठक बस फ़ाइल की सामग्री का उपभोग कर रहे हैं, तो यह ठीक हो सकता है, अगर उन्हें एक ही सामग्री के साथ फ़ाइलों के लिए अलग-अलग इनोड्स मिलते हैं, तो वे बस एक ही खुश होंगे।

हार्ड लिंक का उपयोग करने वाला दूसरा दृष्टिकोण बेहतर दिखता है, लेकिन मुझे याद है कि कई समवर्ती ग्राहकों से एनएफएस पर कड़ी लूप में हार्डलिंक के साथ प्रयोग करना और सफलता की गिनती करना और वहां अभी भी कुछ दौड़ की स्थिति दिख रही थी, जहां ऐसा लगता था कि दो ग्राहकों ने हार्डलिंक जारी किया था एक ही समय में, एक ही गंतव्य के साथ, दोनों सफल होते दिख रहे थे। (यह संभव है कि यह व्यवहार विशेष रूप से NFS सर्वर कार्यान्वयन, YMMV से संबंधित था।) किसी भी मामले में, शायद एक ही तरह की दौड़ की स्थिति है, जहाँ आप उन मामलों में एक ही फ़ाइल के लिए दो अलग-अलग इनोड्स प्राप्त कर सकते हैं, जहाँ भारी है इन दौड़ की स्थिति को ट्रिगर करने के लिए लेखकों के बीच सहमति। यदि आपके लेखक सुसंगत हैं (दोनों ए से बी की नकल कर रहे हैं), और आपके पाठक केवल सामग्री का उपभोग कर रहे हैं, तो यह पर्याप्त हो सकता है।

अंत में, आपने लॉकिंग का उल्लेख किया। दुर्भाग्य से लॉकिंग में गंभीर रूप से कमी है, कम से कम NFSv3 में (NFSv4 के बारे में निश्चित नहीं है, लेकिन मैं शर्त लगाऊंगा या तो अच्छा नहीं है।) यदि आप लॉकिंग पर विचार कर रहे हैं, तो आपको वितरित लॉकिंग के लिए अलग-अलग प्रोटोकॉल में देखना चाहिए, संभवतः बैंड के साथ बाहर। वास्तविक फ़ाइल प्रतियां, लेकिन यह विघटनकारी, जटिल और गतिरोध जैसे मुद्दों से ग्रस्त है, इसलिए मैं कहूंगा कि इससे बचना बेहतर है।


एनएफएस पर परमाणुता के विषय पर अधिक पृष्ठभूमि के लिए, आप मेलडिर मेलबॉक्स प्रारूप पर पढ़ना चाह सकते हैं , जो एनएफएस पर भी ताले से बचने और मज़बूती से काम करने के लिए बनाया गया था। यह हर जगह अद्वितीय फ़ाइल नाम रखकर ऐसा करता है (इसलिए आपको अंत में अंतिम बी भी नहीं मिलता है।)

शायद आपके विशेष मामले में कुछ और दिलचस्प है, Maildir ++ प्रारूप मेलबॉक्स कोटे के लिए समर्थन जोड़ने के लिए Maildir का विस्तार करता है और ऐसा करता है कि मेलबॉक्स के अंदर एक निश्चित नाम के साथ एक फ़ाइल को अद्यतन करके (ताकि आपके B. के करीब हो सकता है) I अपील करने के लिए, जो वास्तव में एनएफएस पर सुरक्षित नहीं है, लेकिन एक पुनर्गणना दृष्टिकोण है जो इस तरह की प्रक्रिया का उपयोग करता है और यह एक परमाणु प्रतिस्थापन के रूप में मान्य है।

उम्मीद है कि ये सभी संकेत उपयोगी होंगे!


2

आप इसके लिए एक प्रोग्राम लिख सकते हैं।

open(O_CREAT|O_RDWD)लक्ष्य फ़ाइल खोलने के लिए उपयोग करें , यह जांचने के लिए सभी बाइट्स और मेटाडेटा पढ़ें कि क्या लक्ष्य फ़ाइल एक पूर्ण है, यदि नहीं, तो दो संभावनाएँ हैं,

  1. अधूरा लिखना

  2. अन्य प्रक्रिया समान कार्यक्रम चला रही है।

लक्ष्य फ़ाइल पर एक खुली फ़ाइल विवरण लॉक को प्राप्त करने का प्रयास करें।

विफलता का मतलब है कि एक समवर्ती प्रक्रिया है, वर्तमान प्रक्रिया मौजूद होनी चाहिए।

सक्सेस का मतलब है कि पिछला लेखन क्रैश हो गया है, आपको फ़ाइल पर लिखकर इसे शुरू करना चाहिए या इसे ठीक करने का प्रयास करना चाहिए।

यह भी ध्यान दें कि fsync()फ़ाइल को बंद करने और लॉक जारी करने से पहले आप लक्ष्य फ़ाइल में लिखने के बाद बेहतर होंगे , या अन्य प्रक्रिया डिस्क-ऑन डेटा को नहीं पढ़ सकती है।

https://www.gnu.org/software/libc/manual/html_node/Open-File-Description-Locks.html

यह एक समवर्ती चलने वाले कार्यक्रम और अंतिम रूप से दुर्घटनाग्रस्त ऑपरेशन के बीच अंतर करने में आपकी सहायता करने के लिए महत्वपूर्ण है।


जानकारी के लिए धन्यवाद, मैं खुद इसे लागू करने के लिए इच्छुक हूं और इसे जारी रखूंगा। मुझे आश्चर्य है कि यह पहले से ही कुछ कोर्यूटिल्स / समान पैकेज के हिस्से के रूप में मौजूद नहीं है!
इवान बेने

यह दृष्टिकोण दुर्घटना की आवश्यकता पर किसी भी आंशिक या भ्रष्ट फ़ाइल B को नहीं छोड़ सकता । फ़ाइल को अस्थायी नाम पर कॉपी करने के मानक दृष्टिकोण का उपयोग करना वास्तव में सबसे अच्छा है, फिर इसे जगह में ले जाना: चाल परमाणु हो सकती है, जो नकल नहीं हो सकती है।
रीइन्टीरियरपोस्ट

@reinierpost यदि क्रैश होता है, लेकिन डेटा पूरी तरह से कॉपी नहीं किया जाता है, तो आंशिक रूप से कॉपी किए गए डेटा को कोई फर्क नहीं छोड़ा जाएगा। लेकिन मेरा दृष्टिकोण इसका पता लगाएगा और इसे ठीक करेगा। फ़ाइल को स्थानांतरित करना परमाणु नहीं हो सकता है, डिस्क क्रॉस भौतिक क्षेत्र में लिखा गया कोई भी डेटा परमाणु नहीं होगा, लेकिन सॉफ्टवेयर (जैसे। ओएस फाइलसिस्टम ड्राइवर, यह दृष्टिकोण) इसे ठीक कर सकता है (यदि आरडब्ल्यू) या एक सुसंगत स्थिति (आरओ) की रिपोर्ट कर सकता है। , जैसा कि प्रश्न के टिप्पणी अनुभाग में उल्लेख किया गया है। साथ ही सवाल नकल के बारे में भी है, न कि हिलाने के लिए।
56

मैंने O_TMPFILE भी देखा, जो शायद मदद करेगा। (और यदि एफएस पर उपलब्ध नहीं है, तो एक त्रुटि का कारण होना चाहिए)
इवान बेने

@ इवान ने क्या आपने दस्तावेज़ पढ़ा है या क्या आपने कभी सोचा है कि O_TMPFILE फाइलसिस्टम समर्थन पर क्यों निर्भर करेगा?
22: 11 德里克

0

तुम एक करके सही परिणाम मिल जाएगा cpके साथ एक साथ mv। यह या तो "ए" की एक नई प्रति के साथ "बी" को बदल देगा, या "बी" को छोड़ देगा जैसा कि पहले था।

cp A B.tmp && mv B.tmp B

मौजूदा को अद्यतन करने के लिए अद्यतन B:

cp A B.tmp && if [ ! -e B ]; then mv B.tmp B; else rm B.tmp; fi

यह 100% परमाणु नहीं है, लेकिन यह करीब हो जाता है। एक दौड़ की स्थिति है जहाँ इनमें से दो चीजें चल रही हैं, दोनों ifएक ही समय में परीक्षण में प्रवेश करते हैं , दोनों देखते हैं कि Bमौजूद नहीं है, फिर दोनों को निष्पादित किया जाता है mv


mv B.tmp B एक पूर्व-मौजूदा B. cp अधिलेखित कर देगा। B.tmp एक पूर्व-विद्यमान B.tmp, दोनों विफलताओं को अधिलेखित कर देगा।
इवान बेने

mv B.tmp Bतब तक नहीं चलेगा जब तक कि cp A B.tmpपहली बार सफलता का परिणाम कोड नहीं मिलता है। वह असफलता कैसे है? इसके अलावा, मैं मानता हूं कि cp A B.tmpएक मौजूदा को अधिलेखित कर देगा B.tmpजो आप करना चाहते हैं। &&गारंटी देता है कि 2 कमांड यदि और केवल यदि पहले एक सामान्य रूप से पूरा करता है चलेंगे।
काण

प्रश्न में सफलता को परिभाषित किया गया है क्योंकि पहले से मौजूद फ़ाइल B. को अधिलेखित नहीं करना B। B का उपयोग करना एक तंत्र है, लेकिन किसी भी पहले से मौजूद फ़ाइल को अधिलेखित नहीं करना चाहिए।
इवान बेने

मैंने अपना उत्तर अपडेट कर दिया। अंततः अगर आपको पूरी तरह से 100% एटमॉसिस की आवश्यकता होती है जब फाइलें मौजूद हो सकती हैं या नहीं हो सकती हैं, और कई थ्रेड्स हैं, तो आपको कहीं एक सिंगल एक्सक्लूसिव लॉक की जरूरत है (एक विशेष फाइल बनाएं, या एक डेटाबेस का उपयोग करें, या ...) जो हर किसी के हिस्से के रूप में अनुसरण करता है। कॉपी / मूव प्रक्रिया।
kaan

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