2009-11-28 22 views
15

कल्पना कीजिए कि आपके पास किसी प्रकार की XML फ़ाइल या कॉन्फ़िगरेशन फ़ाइल के साथ काम करने के लिए लाइब्रेरी है। पुस्तकालय पूरी फ़ाइल को स्मृति में पढ़ता है और सामग्री को संपादित करने के तरीकों को प्रदान करता है। जब आप सामग्री को कुशल बनाते हैं तो आप सामग्री को वापस फ़ाइल में सहेजने के लिए write पर कॉल कर सकते हैं। प्रश्न यह है कि इसे सुरक्षित तरीके से कैसे करें।फ़ाइल को सुरक्षित रूप से कैसे लिखें?

मौजूदा फ़ाइल को ओवरराइट करना (मूल फ़ाइल में लिखना शुरू करना) स्पष्ट रूप से सुरक्षित नहीं है। यदि write विधि पूर्ण होने से पहले विफल हो जाती है तो आप आधे लिखित फ़ाइल के साथ समाप्त हो जाते हैं और आपने डेटा खो दिया है।

एक बेहतर विकल्प कहीं एक अस्थायी फ़ाइल पर लिखने में होगा, और जब write विधि समाप्त हो गया है, तो आप मूल फ़ाइल को अस्थायी फ़ाइल की प्रतिलिपि।

अब, यदि प्रतिलिपि किसी भी तरह विफल हो जाती है, तो आपने अभी भी अस्थायी फ़ाइल में डेटा को सही ढंग से सहेजा है। और यदि प्रति सफल हो जाती है, तो आप अस्थायी फ़ाइल को हटा सकते हैं।

पॉज़िक्स सिस्टम पर मुझे लगता है कि आप rename सिस्टम कॉल का उपयोग कर सकते हैं जो परमाणु ऑपरेशन है। लेकिन आप विंडोज सिस्टम पर यह कैसे करेंगे? विशेष रूप से, आप पायथन का उपयोग करके इस सर्वोत्तम तरीके से कैसे संभालते हैं?

इसके अलावा, फाइलों को सुरक्षित रूप से लिखने के लिए एक और योजना है?

+0

प्रतिलिपि क्यों? नाम क्यों नहीं बदला? –

उत्तर

14

यदि आप पाइथन के दस्तावेज़ को देखते हैं, तो यह स्पष्ट रूप से उल्लेख करता है कि os.rename() एक परमाणु ऑपरेशन है। तो आपके मामले में, एक अस्थायी फ़ाइल में डेटा लिखना और फिर इसे मूल फ़ाइल में नाम देना काफी सुरक्षित होगा।

एक और तरीका है इस तरह काम कर सकता था:

  • मूल फ़ाइल हो abc.xml
  • abc.xml.tmp बना सकते हैं और abc.xml को
  • नाम बदलने abc.xml यह करने के लिए नए डेटा लिखने जाने .bak
  • नाम बदलने abc.xml.tmp abc.xml को
  • के बाद नए abc.xml ठीक से जगह में डाल दिया जाता है, abc.xml.bak
हटाने

जैसा कि आप देख सकते हैं कि आपके पास abc.xml है।आपके साथ बाक जो आप टीएमपी फ़ाइल से संबंधित किसी भी मुद्दे और इसे वापस कॉपी करने के लिए पुनर्स्थापित करने के लिए उपयोग कर सकते हैं।

+0

यह बैकअप फ़ाइल को हटाने के अलावा एसएलॉट के उत्तर के समान है। ऐसा लगता है कि ऐसा करने का सबसे अच्छा तरीका है। धन्यवाद। –

+0

मैंने वास्तव में इस कार्यान्वयन को जिस तरह से ZODB (ज़ोप ऑब्जेक्ट डेटाबेस) अपनी डेटाबेस फ़ाइल (Data.fs) का पैकिंग किया है यानी डेटाबेस फ़ाइल से पुराने लेनदेन की अप्रयुक्त स्थान को हटा रहा है। कोड नियमित पायथन कोड है, पैकिंग एक अस्थायी फ़ाइल में किया जाता है और फिर उपरोक्त के समान चरणों का आयोजन किया जाता है। जेडओडीबी इतने सालों से आसपास रहा है और विंडोज और पॉज़िक्स प्लेटफार्मों पर अच्छी तरह से काम करता है, इसलिए मेरा मानना ​​है कि इस दृष्टिकोण को काम करना चाहिए। –

+3

पायथन उस गारंटी को लागू नहीं कर सकता है जिसका नाम परमाणु होना चाहिए। जहां तक ​​मुझे पता है, यह सिर्फ ओएस सिस्टम कॉल को बुला रहा है। आपके द्वारा दी जाने वाली प्रक्रिया अच्छी तरह से काम करती है। –

4

विन एपीआई में मुझे बहुत अच्छा फ़ंक्शन ReplaceFile मिला जो वैकल्पिक बैक-अप के साथ भी क्या नाम सुझाता है। DeleteFile, MoveFile कॉम्बो के साथ हमेशा रास्ता है।

सामान्य रूप से आप जो करना चाहते हैं वह वाकई अच्छा है। और मैं किसी भी बेहतर लेखन योजना के बारे में नहीं सोच सकता।

+3

यह भी बेहतर होगा कि आप उचित अजगर कोड है कि एमएस पुस्तकालय API कॉल के साथ कि सचित्र होगा। – RedGlyph

+1

मुझे एहसास नहीं हुआ कि ReplaceFile मौजूद था। दस्तावेज़ों को पढ़ना, ऐसा लगता है कि बस नाम बदलने से बहुत कुछ करना है। यह प्रतिस्थापित फ़ाइल के कई गुणों को बनाए रखता है, इसलिए यह विशेष रूप से इस उद्देश्य के लिए डिज़ाइन किया गया है। –

3

एक सरल समाधान। एक अस्थायी फ़ाइल बनाने के लिए tempfile का उपयोग करें और यदि लेखन सफल होता है तो फ़ाइल को अपनी मूल कॉन्फ़िगरेशन फ़ाइल में बस नाम बदलें।

फ़ाइल लॉक करने के लिए, portalocker देखें।

+1

तो tempfile लक्ष्य एक से एक और फाइल सिस्टम में बनाई गई है, तो अंतिम या तो काम नहीं करेगा का नाम बदलने, या यह परमाणु नहीं होगा। – tzot

+0

लेकिन जब तक नाम बदलने परमाणु है, मैं, डेटा खो जोखिम है ना? –

4

मानक समाधान यह है।

  1. इसी तरह के नाम के साथ एक नई फ़ाइल लिखें। उदाहरण के लिए X.ext #।

  2. जब वह फ़ाइल बंद हो गई है (और शायद यहां तक ​​कि पढ़ा और चेकसम किया गया), तो आप दो दो नाम हैं।

    • X.ext (मूल) X.ext को

  3. (केवल पागल के लिए X.ext ~

  4. X.ext # (नया) के लिए पैरानोइड्स) गंदे बफर लिखने के लिए ओएस सिंक फ़ंक्शन को कॉल करें।

किसी भी समय कुछ भी खो या भ्रष्ट नहीं है। नाम के दौरान एकमात्र गड़बड़ हो सकती है। लेकिन आपने कुछ भी खो दिया है या कुछ भी दूषित नहीं किया है। अंतिम नाम बदलने तक मूल ठीक वसूली योग्य है।

+0

लेकिन अगर आप दो बार नाम बदलने नहीं है (एक बैकअप बनाने के रूप में तुमने किया था) आप खो डेटा का जोखिम है, तो नाम बदलने परमाणु नहीं, सही है? –

+1

नाम बदलें * कई ओएस में परमाणु है। हालांकि, ऑपरेशन की परमाणुता के बारे में हाथ-झुकाव से बैकअप अधिक महत्वपूर्ण है। याद रखें: एक दुर्घटना की बाधाएं (विंडोज़ को छोड़कर) बहुत छोटी है। एक नाम के बीच में एक दुर्घटना की बाधाएं (जो केवल कुछ ही निर्देश हैं और एक सिंक बहुत छोटा है। –

11

आप POSIXly सही हो सकता है और बचाने के लिए चाहते हैं तो आप के लिए:

  1. अस्थायी फ़ाइल
  2. फ्लश और fsync फ़ाइल (या fdatasync)
  3. नाम बदलें मूल फ़ाइल से अधिक करने के लिए लिखें

ध्यान दें कि fsync को कॉल करने पर प्रदर्शन पर अप्रत्याशित प्रभाव पड़ता है - ext3 पर लिनक्स डिस्क I/O के परिणामस्वरूप सेकंड की पूरी संख्या के लिए बंद हो सकता है, परिणामस्वरूप अन्य ओ के आधार पर I/O उपयोग करना

सूचना है कि renameनहीं POSIX में एक परमाणु ऑपरेशन है - डेटा दर्ज करने के लिए आपकी अपेक्षा के संबंध में कम से कम नहीं। हालांकि, अधिकांश ऑपरेटिंग सिस्टम और फाइल सिस्टम इस तरह काम करेंगे। लेकिन ऐसा लगता है कि आप Ext4 के बारे में बहुत बड़ी लिनक्स चर्चा चूक गए हैं और फाइल सिस्टम परमाणुता के बारे में गारंटी देता है। मुझे नहीं पता कि वास्तव में कहां लिंक करना है, लेकिन यहां एक शुरुआत है: ext4 and data loss

नोटिस हालांकि कई प्रणालियों पर, नाम की अपेक्षा के अनुसार नाम बदलना सुरक्षित होगा। हालांकि यह संभवतः सभी संभव लिनक्स confiugrations में प्रदर्शन और विश्वसनीयता दोनों पाने के लिए संभव नहीं है!

अस्थायी फ़ाइल को लिखने के साथ, अस्थायी फ़ाइल का नाम बदलने के बाद, कोई उम्मीद करेगा कि संचालन निर्भर हैं और क्रम में निष्पादित किया जाएगा।

हालांकि समस्या यह है कि अधिकांश फाइल सिस्टम मेटाडेटा और डेटा को अलग नहीं करते हैं। एक नाम केवल मेटाडाटा है। यह आपके लिए भयानक लग सकता है, लेकिन फाइल सिस्टम डेटा पर मेटाडेटा मानते हैं (उदाहरण के लिए एचएफएस + या एक्स 3,4 में जर्नलिंग लें)! इसका कारण यह है कि मेटाडेटा हल्का है, और अगर मेटाडाटा दूषित है, तो संपूर्ण फाइल सिस्टम दूषित है - फाइल सिस्टम को निश्चित रूप से स्वयं को संरक्षित करना चाहिए, फिर उस क्रम में उपयोगकर्ता के डेटा को सुरक्षित रखें।

Ext4 ने rename की अपेक्षा तोड़ दी जब यह पहली बार बाहर आया, हालांकि इसे हल करने के लिए हेरिस्टिक को जोड़ा गया। मुद्दा एक असफल नाम है, लेकिन एक सफल नाम है। Ext4 सफलतापूर्वक नाम पंजीकृत कर सकता है, लेकिन इसके बाद जल्द ही एक दुर्घटना आती है तो फ़ाइल डेटा लिखने में विफल रहता है। नतीजा तब 0-लम्बाई फ़ाइल है और न तो संरेखण और न ही नया डेटा।

तो संक्षेप में, POSIX ऐसी कोई गारंटी नहीं देता है। अधिक जानकारी के लिए लिंक किए गए Ext4 आलेख को पढ़ें!

+0

शायद मुझे गलत समझा जाए। लेकिन यदि आप एक पॉज़िक्स सिस्टम पर नाम बदलते हैं, तो क्या आप गारंटी नहीं देते हैं कि अगर नाम बदलता है तो गंतव्य अनमोडिफाइड है? "यदि नाम (ईआईओ] के अलावा किसी भी कारण से नाम बदलें() फ़ंक्शन विफल रहता है, तो नई नाम वाली कोई भी फ़ाइल अप्रभावित होगी।" मुझे लगता है कि यह अभी भी दूषित डेटा छोड़ सकता है। –

+0

समस्या यह है सफल नाम बदलें, और यह सिर्फ एक नाम पूरे ऑपरेशन की परमाणुता की गारंटी नहीं देता है। – u0b34a0f6ae

+1

आपका मतलब यह है कि नाम बदलें() चेकपॉइंट नहीं करता है। यह निश्चित रूप से परमाणु है, देखें: http://www.opengroup.org/onlinepubs /009695399/functions/rename.html – geocar

1

प्रति RedGlyph के सुझाव, मैं ReplaceFile के एक कार्यान्वयन विंडोज API को एक्सेस करने ctypes का उपयोग करता है जोड़ा कर रहा हूँ। मैंने पहले इसे jaraco.windows.api.filesystem में जोड़ा।

ReplaceFile = windll.kernel32.ReplaceFileW 
ReplaceFile.restype = BOOL 
ReplaceFile.argtypes = [ 
    LPWSTR, 
    LPWSTR, 
    LPWSTR, 
    DWORD, 
    LPVOID, 
    LPVOID, 
    ] 

REPLACEFILE_WRITE_THROUGH = 0x1 
REPLACEFILE_IGNORE_MERGE_ERRORS = 0x2 
REPLACEFILE_IGNORE_ACL_ERRORS = 0x4 

मैंने तब इस स्क्रिप्ट का उपयोग करके व्यवहार का परीक्षण किया।

from jaraco.windows.api.filesystem import ReplaceFile 
import os 

open('orig-file', 'w').write('some content') 
open('replacing-file', 'w').write('new content') 
ReplaceFile('orig-file', 'replacing-file', 'orig-backup', 0, 0, 0) 
assert open('orig-file').read() == 'new content' 
assert open('orig-backup').read() == 'some content' 
assert not os.path.exists('replacing-file') 

Windows में यह केवल काम करता है, यह अच्छा सुविधाओं की एक बहुत कुछ है कि अन्य की जगह दिनचर्या की कमी होगी के लिए प्रकट होता है। विवरण के लिए API docs देखें।

import fileinput 
for line in fileinput.input(filename,inplace=True, backup='.bak'): 
    # inplace=True causes the original file to be moved to a backup 
    # standard output is redirected to the original file. 
    # backup='.bak' specifies the extension for the backup file. 

    # manipulate line 
    newline=process(line) 
    print(newline) 

, तो आप सारी सामग्री में पढ़ने के लिए इससे पहले कि आप नई पंक्ति के लिख सकते हैं की जरूरत है आप:

0

आप fileinput मॉड्यूल समर्थन हुआ और यथा-स्थान आप के लिए लेखन को संभालने के लिए इस्तेमाल कर सकते हैं क्या कर सकते हैं कि पहले तो

newcontents=process(contents) 
for line in fileinput.input(filename,inplace=True, backup='.bak'): 
    print(newcontents) 
    break 

के साथ पूरे नई सामग्री मुद्रित स्क्रिप्ट अचानक समाप्त हो जाती हैं, तो आप अभी भी बैकअप होगा।

3

अब एक कोडिफाइड, शुद्ध पायथन है, और मुझे boltons utility library: boltons.fileutils.atomic_save में पाइथोनिक समाधान कहने की हिम्मत है।

बस pip install boltons, तो:

from boltons.fileutils import atomic_save 

with atomic_save('/path/to/file.txt') as f: 
    f.write('this will only overwrite if it succeeds!\n') 

व्यावहारिक विकल्पों में से एक बहुत, all well-documented रहे हैं। पूर्ण प्रकटीकरण, मैं बोल्टन का लेखक हूं, लेकिन यह विशेष हिस्सा बहुत सामुदायिक सहायता के साथ बनाया गया था। अगर कुछ अस्पष्ट है तो drop a note में संकोच न करें!

संबंधित मुद्दे