विशाल फ़ाइल के आरंभ और अंत में लाइनें जोड़ें


23

मेरे पास ऐसा परिदृश्य है जहां बड़ी फ़ाइलों की भीग और अंत पर लाइनें जोड़ी जाती हैं।

मैंने नीचे दिखाए अनुसार कोशिश की है।

  • पहली पंक्ति के लिए:

    sed -i '1i\'"$FirstLine" $Filename
  • अंतिम पंक्ति के लिए:

    sed -i '$ a\'"$Lastline" $Filename  

लेकिन इस आदेश के साथ मुद्दा यह है कि यह फ़ाइल की पहली पंक्ति को जोड़ रहा है और संपूर्ण फ़ाइल को ट्रैवर्स कर रहा है। अंतिम लाइन के लिए यह फिर से पूरी फाइल को ट्रेस करके अंतिम लाइन को जोड़ रहा है। इसकी बहुत बड़ी फ़ाइल (14GB) के बाद से यह बहुत लंबा समय ले रहा है।

मैं केवल एक बार फ़ाइल को पढ़ने के दौरान शुरुआत में और एक फ़ाइल के अंत में एक पंक्ति कैसे जोड़ सकता हूं?

जवाबों:


20

sed -iएक कार्यान्वयन विवरण के रूप में टेम्पेफाइल्स का उपयोग करता है, जो कि आप अनुभव कर रहे हैं; हालाँकि, मौजूदा सामग्रियों को अधिलेखित किए बिना डेटा स्ट्रीम की शुरुआत में डेटा को प्रस्तुत करने के लिए फ़ाइल को फिर से लिखना पड़ता है, इससे बचने के लिए भी कोई रास्ता नहीं है sed -i

यदि फ़ाइल को फिर से लिखना एक विकल्प नहीं है, तो आप उदाहरण के लिए, पढ़ने पर इसे हेरफेर करने पर विचार कर सकते हैं:

{ echo some prepended text ; cat file ; } | command

इसके अलावा, धारा संपादन की धाराओं के लिए है - एक फ़ाइल एक स्ट्रीम नहीं है। एक प्रोग्राम का उपयोग करें जो इस उद्देश्य के लिए है, जैसे कि एड या एक्स। -iसीड करने का विकल्प न केवल पोर्टेबल है, यह आपकी फ़ाइल के किसी भी सीलिंक को भी तोड़ देगा, क्योंकि यह अनिवार्य रूप से इसे हटा देता है और इसे फिर से बनाता है, जो कि व्यर्थ है।

आप इस तरह से एक ही आदेश में ऐसा कर सकते हैं ed:

ed -s file << 'EOF'
0a
prepend these lines
to the beginning
.
$a
append these lines
to the end
.
w
EOF

ध्यान दें कि आपके कार्यान्वयन के आधार पर, यह एक पेजिंग फ़ाइल का उपयोग कर सकता है, जिसके लिए आपको कम से कम इतना स्थान उपलब्ध होना चाहिए।


हाय, एड कमांड जो यू प्रदान करता है वह बड़ी फ़ाइलों के लिए बहुत अच्छी तरह से काम कर रहा है। लेकिन मेरे पास टेस्ट, टेस्ट 1, टेस्ट 2 जैसी 3 विशाल फाइलें हैं। मैंने ed -s Tes * जैसी कमांड दी। << 'EOF' 0a शुरुआत में इन लाइनों को प्रस्तुत करता है। $ इन पंक्तियों को अंत तक जोड़ दें। w ईओएफ लेकिन इसकी केवल टेस्ट फाइल लेना और पहली / आखिरी लाइनों को जोड़ना। हम एक ही कमांड में बदलाव कैसे कर सकते हैं ताकि इसे सभी फाइलों में पहली और आखिरी पंक्ति जोड़नी पड़े।
UNIXbest

@UNIXbest - एक forलूप का उपयोग करें :for file in Tes*; do [command]; done
क्रिस डाउन

हाय डाउन, मैंने टेस * में फ़ाइल के लिए कमांड का उपयोग किया है; do ed -s Tes * << 'EOF' 0a HEllO HDR $ एक हैलो टीएलआर। डब्ल्यू ईओएफ ने किया लेकिन इसकी पहली फाइल में अभी भी लिख रहा है।
UNIXbest

सही, क्योंकि आपको उपयोग करने की आवश्यकता है "$file", न Tes*कि तर्क के रूप में ed
क्रिस डाउन

2
@UNIXbest यदि आपकी समस्या इस उत्तर से हल हो गई है, तो आपको इसे स्वीकार करने पर विचार करना चाहिए।
जोसेफ आर।

9

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

sed '
1i\
begin
$a\
end' < file 1<> file

यह इस तथ्य का उपयोग करता है कि जब इसकी स्टड / स्टडआउट एक फाइल होती है, तो sed ब्लॉक द्वारा पढ़ता और लिखता है। तो यहाँ, इसके लिए यह ठीक है कि यह जिस फ़ाइल को पहली पंक्ति में जोड़ रहा है, उसे ओवरराइड करने के लिए यह sedब्लॉक के आकार (4k या 8k की तरह कुछ होना चाहिए) से छोटा है।

ध्यान दें कि यदि किसी कारणवश sedविफल हो जाता है (मारे गए, मशीन क्रैश ...), तो आप आधी संसाधित फ़ाइल के साथ समाप्त हो जाएंगे, जिसका अर्थ होगा कि कुछ डेटा बीच में कहीं गायब होने वाली पहली पंक्ति का आकार है।

यह भी ध्यान दें कि जब तक आपका sedGNU sedनहीं है, बाइनरी डेटा के लिए काम नहीं करेगा (लेकिन जब से आप उपयोग -iकर रहे हैं, आप GNU sed का उपयोग कर रहे हैं)।


उबंटू 16.04 पर मेरे लिए यह त्रुटियां
Csaba Toth

4

यहां कुछ विकल्प दिए गए हैं (जिनमें से सभी फ़ाइल की एक नई प्रतिलिपि बनाएंगे ताकि सुनिश्चित करें कि आपके पास उसके लिए पर्याप्त जगह है):

  • सरल गूंज / बिल्ली

    echo "first" > new_file; cat $File >> new_file; \
      echo "last" >> new_file; 
    
  • awk / gawk आदि

    gawk 'BEGIN{print "first\n"}{print}END{print "last\n"}' $File > NewFile 

    awkऔर इसका ilk लाइन द्वारा फाइल लाइन पढ़ता है। BEGIN{}ब्लॉक पहली पंक्ति और पहले निष्पादित किया जाता है END{}अंतिम पंक्ति के बाद ब्लॉक। तो, ऊपर दिए गए आदेश का मतलब है print "first" at the beginning, then print every line in the file and print "last" at the end

  • पर्ल

    perl -ne 'BEGIN{print "first\n"} print;END{print "last\n"}' $File > NewFile

    यह अनिवार्य रूप से पर्ल के ऊपर लिखी गव के समान ही है।


1
ध्यान दें कि इन सभी मामलों में, आपको नई फ़ाइल के लिए कम से कम 14GB अधिक स्थान की आवश्यकता होगी।
क्रिस डाउन

@ChrisDown अच्छी बात है, मैंने अपना उत्तर उस स्पष्ट करने के लिए संपादित किया। मैंने मान लिया कि यह कोई समस्या नहीं थी क्योंकि ओपी उपयोग कर रहा था sed -iजो कि अस्थायी फाइलें बनाता है।
terdon

3

मैं बहुत सरल पसंद करते हैं:

gsed -i '1s/^/foo\n/gm; $s/$/\nbar/gm' filename.txt

यह फ़ाइल को रूपांतरित करता है:

asdf
qwer

फ़ाइल में:

foo
asdf
qwer
bar

2

आप पूर्व मोड में विम का उपयोग कर सकते हैं:

ex -sc '1i|ALFA' -c '$a|BRAVO' -cx file
  1. 1 पहली पंक्ति का चयन करें

  2. i टेक्स्ट और न्यूलाइन डालें

  3. $ अंतिम पंक्ति का चयन करें

  4. a पाठ और newline जोड़ें

  5. x सहेजें और बंद करें


क्या होगा अगर हम इसे कई फाइलों में करना चाहते हैं?
जियोय्स

1
@geoyws जो वास्तव में इस सवाल के दायरे में नहीं हैं
स्टीवन पेनी

क्या आप सुनिश्चित हैं कि $ a और%% a नहीं है?
कार्लोस रोबल्स

2

किसी फ़ाइल की शुरुआत में डेटा डालने का कोई तरीका नहीं है, आप बस एक नई फ़ाइल बना सकते हैं, अतिरिक्त डेटा लिख ​​सकते हैं, और पुराने डेटा को जोड़ सकते हैं। तो आपको पहली पंक्ति सम्मिलित करने के लिए कम से कम एक बार पूरी फ़ाइल को फिर से लिखना होगा। आप हालांकि फ़ाइल को फिर से लिखे बिना अंतिम पंक्ति को जोड़ सकते हैं।

sed -i '1i\'"$FirstLine" $Filename
echo "$LastLine" >>$Filename

वैकल्पिक रूप से, आप दो कमांड को एक रन ऑफ सेड में जोड़ सकते हैं।

sed -i -e '1i\'"$FirstLine" -e '$ a\'"$Lastline" $Filename

sed -iएक नई आउटपुट फ़ाइल बनाता है और फिर इसे पुरानी फ़ाइल पर ले जाता है। इसका मतलब यह है कि जब सेड काम कर रहा होता है, तो स्पेस का उपयोग करके फाइल की दूसरी कॉपी होती है। आप फ़ाइल को स्थान पर अधिलेखित करके इससे बच सकते हैं , लेकिन प्रमुख प्रतिबंधों के साथ: आपके द्वारा जोड़ी जा रही पंक्ति, sed की बफर से छोटी होनी चाहिए, और यदि आपका सिस्टम क्रैश हो जाता है तो आप एक क्षतिग्रस्त फ़ाइल और कुछ सामग्री के साथ समाप्त हो जाएंगे। मध्य, इसलिए मैं इसके खिलाफ दृढ़ता से अनुशंसा करता हूं।

¹ लिनक्स में डेटा को फाइल में डालने का एक तरीका है, लेकिन यह केवल एक संख्या में फाइलसिस्टम ब्लॉक को सम्मिलित कर सकता है, यह मनमानी लंबाई के तार नहीं डाल सकता है। यह कुछ अनुप्रयोगों के लिए उपयोगी है, जैसे डेटाबेस और वर्चुअल मशीन, लेकिन यह पाठ फ़ाइलों के लिए बेकार है।


सच नहीं। पर देखो fallocate()के साथ FALLOC_FL_INSERT_RANGEआधुनिक कर्नेल में XFS पर उपलब्ध और ext4 (4.xx) man7.org/linux/man-pages/man2/fallocate.2.html
एरिक

@ एरिक आप केवल पूरे ब्लॉक सम्मिलित कर सकते हैं, हालांकि, मनमाने ढंग से बाइट लंबाई नहीं, कम से कम लिनक्स 4.15.0 के साथ ext4 के रूप में। क्या कोई फाइल सिस्टम है जो मनमाने बाइट लंबाई को सम्मिलित कर सकता है?
गाइल्स का SO- बुराई होना बंद करो '

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

0
$ (echo "Some Text" ; cat file1) > file2

4
केवल कोड उत्तर स्वीकार्य नहीं हैं, कृपया अपना उत्तर
सुधारें

अपने सुझाव, या प्रलेखन के लिंक को शामिल करने के लिए अपने उत्तर का विस्तार करने पर विचार करें जो आपके समाधान का समर्थन करते हैं।
HalosGhost

-1

आधुनिक लिनक्स कर्नेल (4.1 या 4.2 से अधिक) एक फाइल की शुरुआत में डेटा को ext4 और xfs फाइल सिस्टम के fallocate()साथ सिस्टम कॉल के माध्यम से डालने का समर्थन करता है FALLOC_FL_INSERT_RANGE। संक्षेप में, यह एक तार्किक स्थानांतरण ऑपरेशन है: डेटा तार्किक रूप से एक उच्च ऑफसेट पर स्थानांतरित किया जाता है।

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

मैं किसी भी आसानी से उपलब्ध लिनक्स उपयोगिता के बारे में नहीं जानता जो फ़ाइल एक्सटेंशन को हेरफेर करता है लेकिन यह लिखना मुश्किल नहीं है: एक फ़ाइल विवरणक प्राप्त करें और fallocate()उचित तर्कों के साथ कॉल करें । अधिक जानकारी के लिए, fallocateसिस्टम कॉल के मैन पेज को देखें : http://man7.org/linux/man-pages/man2/fallocate.2.html


एक उपयोगिता समस्या नहीं है (एक गैर-एम्बेडेड लिनक्स मानकर): उपयोग-लिनक्स में एक fallocateउपयोगिता है। समस्या यह है कि पूरे ब्लॉकों की एक ग्रैन्युलैरिटी अधिकांश पाठ फ़ाइलों के लिए इसे बेकार बना देती है। एक अन्य समस्या यह है कि रेंज आवंटन और बाद के संशोधन परमाणु नहीं हैं। तो यह वास्तव में यहाँ समस्या का समाधान नहीं है।
गाइल्स का SO- बुराई होना बंद हो '

दानेदारता एक चेतावनी है जिसे मैंने पहले ही उल्लेख किया है और नहीं, यह इसे बेकार नहीं बनाता है, यह आवेदन पर निर्भर करता है। आपने इस प्रश्न में कहां देखा कि परमाणु महत्वपूर्ण है? मैं केवल प्रदर्शन की समस्या देख सकता हूं। यहाँ तक कि यह syscall परमाणु प्रतीत होता है: elixir.bootlin.com/linux/latest/source/fs/open.c#L228 और अगर परमाणु महत्वपूर्ण हो जाता है (यह नहीं है, लेकिन यह तर्क के लिए है) बस फ़ाइल लॉकिंग का उपयोग करें। (मुझे कर्नेल कोड में उस स्थान पर इंगित करें जहां fallocateएटमॉसिटी टूटी हुई है, मैं उत्सुक हूं)
एरिक
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.