सवाल लिनक्स: एक ही समय में इनपुट और आउटपुट के रूप में फ़ाइल का उपयोग कैसे करें?


मैंने बस निम्नलिखित में बैश में भाग लिया है:

uniq .bash_history > .bash_history

और मेरी इतिहास फ़ाइल पूरी तरह खाली हो गई।

मुझे लगता है कि मुझे लिखने से पहले पूरी फाइल को पढ़ने का एक तरीका चाहिए। यह कैसे किया जाता है?

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


45
2018-04-24 18:32


मूल


ऐसा इसलिए है क्योंकि फ़ाइलों को दाएं से बाएं से खोला जाता है। यह भी देखें stackoverflow.com/questions/146435/... - kaerast
आपको आउटपुट को एक ही निर्देशिका में एक नई फाइल में लिखना होगा और पुरानी फ़ाइल के शीर्ष पर उसका नाम बदलना होगा। कोई अन्य दृष्टिकोण आपके डेटा को खोने का जोखिम उठाएगा यदि यह आधे रास्ते में बाधित है। कुछ टूल आपके द्वारा इस चरण को छुपा सकते हैं। - kasperd
या, bash यदि आप अनदेखा करने के लिए HISTCONTROL सेट करते हैं तो अपने इतिहास में लगातार डुप्लिकेट नहीं रखेंगे; मैनपेज देखें। - dave_thompson_085


जवाब:


मैं उपयोग करने की सलाह देते हैं sponge से moreutils। मैनपेज से:

DESCRIPTION
  sponge  reads  standard  input  and writes it out to the specified file. Unlike
  a shell redirect, sponge soaks up all its input before opening the output file.
  This allows for constructing pipelines that read from and write to the same 
  file.

अपनी समस्या पर इसे लागू करने के लिए, कोशिश करें:

uniq .bash_history | sponge .bash_history

41
2018-04-24 22:46



यह बिल्ली की तरह है, लेकिन चूसने की क्षमताओं के साथ: डी - MilliaLover


मैं बस एक और जवाब देना चाहता था जो सरल है, और स्पंज का उपयोग नहीं करता है (क्योंकि इसे अक्सर हल्के वातावरण में शामिल नहीं किया जाता है)।

echo "$(uniq .bash_history)" > .bash_history

वांछित परिणाम होना चाहिए। उपशीर्षक को पहले निष्पादित किया जाता है। लेखन के लिए bash_history खोला जाता है। फिल पी के जवाब में समझाया गया है, उस समय तक .bash_history मूल कमांड में पढ़ा जाता है, इसे पहले ही '>' ऑपरेटर द्वारा छोटा कर दिया गया है।


60
2017-10-20 08:46



मैं आम तौर पर उत्तर का प्रशंसक नहीं हूं जो एक प्राचीन प्रश्न को खारिज कर देता है जिसमें पहले से ही एक वैध, स्वीकार्य उत्तर है - लेकिन यह सुरुचिपूर्ण, अच्छी तरह से लिखा गया है, और इसकी आवश्यकता (हल्के वातावरण) के लिए एक मजबूत तर्क बनाता है; मेरे लिए, यह वास्तव में उत्तरों के मौजूदा सेट में कुछ जोड़ता है। एसएफ, हार्ट में आपका स्वागत है (आप यहां एक महीने रहे हैं, लेकिन मुझे लगता है कि यह आपकी पहली वास्तविक पोस्टिंग है)। मैं आपको इस तरह से अधिक जवाब पढ़ने की उम्मीद करता हूं! - MadHatter
यह सबसे अच्छा समाधान है। मुझे एक सबहेल का उपयोग करना पड़ा $() कुछ बचने के मुद्दों के कारण बैकटिक्स की बजाय। - CMCDragonkai
मैं सोच रहा हूं कि क्या यह समाधान बड़ी फाइलों के लिए स्केल करता है, कहें ... 20 या 50 जीबी। - Amit Naidu
यह वास्तव में स्वीकार्य जवाब होना चाहिए। - maxywb
मैं सहमत हूँ। मैंने स्वीकार किए गए उत्तर को ध्वजांकित किया है, इसलिए उम्मीद है कि कोई इसे ओवरराइड करेगा। - Ced


समस्या यह है कि कमांड चलाने से पहले आपका खोल कमांड पाइपलाइन सेट कर रहा है। यह "इनपुट और आउटपुट" का मामला नहीं है, यह है कि यूनिक के चलने से पहले फ़ाइल की सामग्री पहले से ही चली गई है। यह कुछ ऐसा होता है:

  1. खोल खोलता है > लेखन के लिए आउटपुट फ़ाइल, इसे छंटनी
  2. उस आउटपुट के लिए फ़ाइल-डिस्क्रिप्टर 1 (stdout के लिए) खोलने के लिए सेट का उपयोग किया जाता है
  3. खोल uniq निष्पादित करता है, शायद execlp ("uniq", "uniq", ".bash_history", NULL) जैसे कुछ
  4. uniq रन, खुलता है। bash_history और वहां कुछ भी नहीं मिला

इन-प्लेस संपादन और अस्थायी फ़ाइल उपयोग सहित कई समाधान हैं, जिनमें अन्य लोग उल्लेख करते हैं, लेकिन समस्या को समझना महत्वपूर्ण है, वास्तव में क्या गलत हो रहा है और क्यों।


11
2018-04-24 23:36





से स्पंज का प्रयोग करें moreutils

uniq .bash_history | sponge .bash_history

6
2018-04-24 22:46





इसका उपयोग किए बिना ऐसा करने के लिए एक और चाल sponge, निम्न आदेश है:

{ rm .bash_history && uniq > .bash_history; } < .bash_history

यह उत्कृष्ट लेख में वर्णित धोखाधड़ी में से एक है फाइलों के "जगह" संपादन backreference.org पर।

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

इस समाधान का नुकसान: यदि uniq किसी कारण से विफल रहता है, आपका इतिहास समाप्त हो जाएगा।


6
2017-11-26 15:49





इस sed स्क्रिप्ट निकट डुप्लीकेट हटा देता है। उसके साथ -i विकल्प, यह जगह में संशोधन करता है। यह से है sed  info फ़ाइल:

sed -i 'h;:b;$b;N;/^\(.*\)\n\1$/ {g;bb};$b;P;D' .bash_history

3
2018-04-24 19:39



sed अभी भी temp फ़ाइल का उपयोग करता है, के साथ एक जवाब जोड़ा strace चित्रण (यह वास्तव में महत्वपूर्ण नहीं है) :-) - Kyle Brandt♦
@ केली: सच है, लेकिन "दृष्टि से बाहर, दिमाग से बाहर"। व्यक्तिगत रूप से, मैं कुछ अस्थायी फ़ाइल का उपयोग करने के बाद से उपयोग करता हूं process input > tmp && mv tmp input उपयोग करने से कहीं अधिक सरल और अधिक पठनीय है sed एक temp फ़ाइल से बचने के लिए बस चालबाजी और यह विफल होने पर यह मेरे मूल को ओवरराइट नहीं करेगा (मुझे नहीं पता कि क्या sed -i कृपा से विफल रहता है - मुझे लगता है कि यह हालांकि होगा)। इसके अलावा, आउटपुट-टू-टेम्प-फ़ाइल विधि के साथ आप बहुत सी चीजें कर सकते हैं जिन्हें बिना किसी और चीज़ के शामिल किया जा सकता है sed स्क्रिप्ट। मुझे पता है कि आप यह सब जानते हैं, लेकिन इससे कुछ दर्शक को फायदा हो सकता है। - Dennis Williamson


एक दिलचस्प tidbit के रूप में, sed एक temp फ़ाइल का भी उपयोग करता है (यह सिर्फ आपके लिए करता है):

$ strace sed -i 's/foo/bar/g' foo    
open("foo", O_RDONLY|O_LARGEFILE)       = 3
...
open("./sedPmPv9z", O_RDWR|O_CREAT|O_EXCL|O_LARGEFILE, 0600) = 4
...
read(3, "foo\n"..., 4096)               = 4
write(4, "bar\n"..., 4)                 = 4
read(3, ""..., 4096)                    = 0
close(3)                                = 0
close(4)                                = 0
rename("./sedPmPv9z", "foo")            = 0
close(1)                                = 0
close(2)                                = 0

विवरण:
Tempfile ./sedPmPv9z एफडी 4 बन जाता है, और foo फाइलें एफडी 3 बन जाती हैं। पढ़ने के ऑपरेशन एफडी 3 पर हैं, और एफडी 4 (अस्थायी फ़ाइल) पर लिखते हैं। फिर foo फ़ाइल को नाम बदलें में temp फ़ाइल के साथ ओवरराइट किया जाता है।


3
2018-04-25 01:12