मैं किसी पाठ फ़ाइल की पहली पंक्ति को bash / sed स्क्रिप्ट का उपयोग करके कैसे निकाल सकता हूं?


554

मुझे बैश स्क्रिप्ट का उपयोग करके एक विशाल टेक्स्ट फ़ाइल से पहली पंक्ति को बार-बार निकालने की आवश्यकता है।

अभी मैं उपयोग कर रहा हूं sed -i -e "1d" $FILE- लेकिन डिलीट करने में लगभग एक मिनट का समय लगता है।

क्या इसे पूरा करने का एक अधिक कुशल तरीका है?


क्या मैं के लिए खड़ा है?
cikatomo

4
@cikatomo: यह इनलाइन एडिट के लिए खड़ा है - यह फाइल को एडिट करता है जो भी आप जेनरेट करते हैं।
drewrockshard

4
पूंछ sed की तुलना में बहुत छोटा है। पूंछ को 13.5s, sed को 0.85s की जरूरत है। मेरी फाइल में ~ 1M लाइन्स, ~ 100MB है। एसएसडी के साथ मैकबुक एयर 2013।
jcsahnwaldt का कहना है कि GoFundMonica

जवाबों:


1029

पूंछ का प्रयास करें :

tail -n +2 "$FILE"

-n x: बस आखिरी xलाइनें प्रिंट करें । tail -n 5आपको इनपुट की अंतिम 5 लाइनें देगा। +उलट के हस्ताक्षर तरह तर्क और मेकअप tailप्रिंट कुछ भी लेकिन पहले x-1लाइनों। tail -n +1पूरी फ़ाइल, tail -n +2सब कुछ लेकिन पहली पंक्ति, आदि को प्रिंट करेगा ।

जीएनयू tailकी तुलना में बहुत तेज है sedtailबीएसडी पर भी उपलब्ध है और -n +2झंडा दोनों उपकरणों के अनुरूप है। अधिक के लिए FreeBSD या OS X मैन पेज देखें।

बीएसडी संस्करण sedहालांकि की तुलना में बहुत धीमा हो सकता है । मुझे आश्चर्य है कि वे कैसे कामयाब रहे; tailबस एक फ़ाइल लाइन को लाइन से पढ़ना चाहिए, जबकि sedएक स्क्रिप्ट की व्याख्या करने, नियमित अभिव्यक्ति और पसंद को शामिल करने वाले सुंदर जटिल संचालन होते हैं।

नोट: आप का उपयोग करने के लिए परीक्षा हो सकती है

# THIS WILL GIVE YOU AN EMPTY FILE!
tail -n +2 "$FILE" > "$FILE"

लेकिन यह आपको एक खाली फाइल देगा । कारण यह है कि पुनर्निर्देशन ( >) tailशेल द्वारा लागू किए जाने से पहले होता है:

  1. शैल ट्रंकट्स फ़ाइल $FILE
  2. शेल के लिए एक नई प्रक्रिया बनाता है tail
  3. शेल पुनर्निर्देशन की tailप्रक्रिया को रोक देता है$FILE
  4. tail अभी खाली से पढ़ता है $FILE

यदि आप फ़ाइल के अंदर पहली पंक्ति को हटाना चाहते हैं, तो आपको उपयोग करना चाहिए:

tail -n +2 "$FILE" > "$FILE.tmp" && mv "$FILE.tmp" "$FILE"

यह &&सुनिश्चित करेगा कि समस्या होने पर फ़ाइल अधिलेखित न हो।


3
इस ss64.com/bash/tail.html के अनुसार , -rविकल्प के साथ BSD 'टेल' का उपयोग करते समय विशिष्ट बफ़र 32k में बदल जाता है। हो सकता है कि सिस्टम में कहीं बफर सेटिंग हो? या -nएक 32-बिट हस्ताक्षरित संख्या है?
यज़्मीर रामिरेज़

41
@Eddie: user869097 कहा कि जब एक यह काम नहीं करता एकल लाइन 15MB या अधिक है। जब तक लाइनें छोटी tailहोंगी , किसी भी फ़ाइल आकार के लिए काम करेंगी।
आरोन दिगुल्ला

6
क्या आप इन तर्कों को समझा सकते हैं?
ड्रीमपूफ

17
@Dreampuf - मैन पेज से:-n N means output the last N lines, instead of the last 10; or use +N to output lines starting with the Nth
विल शेपर्ड

11
मैं परिमाण के एक क्रम से @JonaChristopherSahnwaldt - पूँछ बहुत कुछ है, जो बहुत ही धीमी है। मैं इसे 500,000K लाइनों (प्रति पंक्ति 50 से अधिक चार्ट नहीं) की फ़ाइल पर परीक्षण कर रहा हूं। हालांकि, मुझे तब एहसास हुआ कि मैं पूंछ के FreeBSD संस्करण का उपयोग कर रहा था (जो डिफ़ॉल्ट रूप से OS X के साथ आता है)। जब मैंने GNU टेल पर स्विच किया, तो टेल कॉल sed कॉल (और GNU sed कॉल, भी) से 10 गुना तेज थी। यदि आप GNU का उपयोग कर रहे हैं, तो AaronDigulla यहाँ सही है।
डैन गुयेन

179

आप '>' ऑपरेटर का उपयोग किए बिना फ़ाइल को अपडेट करने के लिए -i का उपयोग कर सकते हैं। निम्न आदेश फ़ाइल से पहली पंक्ति को हटा देगा और इसे फ़ाइल में सहेज देगा।

sed -i '1d' filename

1
मुझे त्रुटि मिलती है:unterminated transform source string
डैनियल कोबे

10
यह हर बार काम करता है और वास्तव में शीर्ष उत्तर होना चाहिए!
xtheking

4
बस याद रखने के लिए, मैक को इन-प्लेस एडिट के साथ सेड का उपयोग करते समय एक प्रत्यय की आवश्यकता होती है। तो ऊपर के साथ चलाएं -i.bak
mjp

3
बस एक नोट - कई पंक्तियों का उपयोग करने के लिएsed -i '1,2d' filename
द गॉडफादर

4
यह संस्करण वास्तव में बहुत अधिक पठनीय है, और अधिक सार्वभौमिक है, की तुलना में tail -n +2। यकीन नहीं होता कि यह शीर्ष उत्तर क्यों नहीं है।
ल्यूक डेविस

74

जो लोग गैर-जीएनयू हैं जो सनोस पर हैं, उनके लिए निम्नलिखित कोड मदद करेगा:

sed '1d' test.dat > tmp.dat 

18
दिलचस्प जनसांख्यिकीय
कप्तान

17

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

लेकिन आपका प्रश्न एक ही समस्या से ग्रस्त है, क्योंकि इसमें कई अन्य लोग इस समाधान को पूर्व-दबाते हैं। यदि आप हमें विस्तार से बताना चाहते हैं कि आप क्या करने की कोशिश कर रहे हैं तो कैसे , हम एक बेहतर विकल्प सुझा सकते हैं।

उदाहरण के लिए, यदि यह एक फ़ाइल A है जो कुछ अन्य प्रोग्राम B प्रक्रियाएँ करता है, तो एक समाधान यह होगा कि पहली पंक्ति को अलग न किया जाए, लेकिन इसे अलग तरीके से संसाधित करने के लिए प्रोग्राम B को संशोधित करें।

मान लें कि आपके सभी प्रोग्राम इस फ़ाइल में संलग्न हैं ए और प्रोग्राम बी वर्तमान में इसे हटाने से पहले पहली पंक्ति को पढ़ता है और संसाधित करता है।

आप प्रोग्राम बी को री-इंजीनियर कर सकते हैं ताकि यह पहली पंक्ति को हटाने की कोशिश न करे, लेकिन फ़ाइल A में एक निरंतर (शायद फ़ाइल-आधारित) ऑफसेट को बनाए रखता है ताकि, अगली बार जब यह चलता है, तो यह उस ऑफसेट, प्रक्रिया की तलाश कर सके। वहाँ लाइन, और ऑफसेट अद्यतन।

फिर, एक शांत समय (मध्यरात्रि?) पर, यह वर्तमान में संसाधित सभी लाइनों को हटाने और ऑफसेट को 0 पर सेट करने के लिए फ़ाइल ए की विशेष प्रसंस्करण कर सकता है।

यह निश्चित रूप से एक कार्यक्रम को खोलने और फिर से लिखने के बजाय एक फ़ाइल खोलने और खोजने के लिए तेज़ होगा। यह चर्चा मानती है कि प्रोग्राम बी पर आपका नियंत्रण है। मुझे नहीं पता कि अगर ऐसा है, लेकिन अन्य संभावित समाधान हो सकते हैं यदि आप अधिक जानकारी प्रदान करते हैं।


मुझे लगता है कि ओपी यह हासिल करने की कोशिश कर रहा है जिससे मुझे यह सवाल मिल सके। मेरे पास प्रत्येक में 500k लाइनों के साथ 10 CSV फाइलें हैं। हर फाइल में पहली पंक्ति की तरह ही हेडर पंक्ति होती है। मैं बिल्ली हूं: इन फाइलों को एक फाइल में इंगेज करता हूं और फिर डीबी में इंपोर्ट करके डीबी को पहली लाइन से कॉलम नाम बनाता हूं। जाहिर है मैं नहीं चाहता कि वह लाइन 2-10 फाइल में दोहराई जाए।
db

1
@db उस मामले में, awk FNR-1 *.csvशायद तेज है।
जिनावी

10

आप फ़ाइलों को जगह में संपादित कर सकते हैं : बस पर्ल के -iझंडे का उपयोग करें , जैसे:

perl -ni -e 'print unless $. == 1' filename.txt

यह पहली पंक्ति को गायब कर देता है, जैसा कि आप पूछते हैं। पर्ल को पूरी फाइल को पढ़ने और कॉपी करने की आवश्यकता होगी, लेकिन यह आउटपुट को मूल फ़ाइल के नाम से सहेजने की व्यवस्था करता है।


10

आप इसे आसानी से कर सकते हैं:

cat filename | sed 1d > filename_without_first_line

कमांड लाइन पर; या फ़ाइल की पहली पंक्ति को स्थायी रूप से हटाने के लिए, -iफ़्लैग के साथ इन-प्लेस मोड का उपयोग करें :

sed -i 1d <filename>

9

जैसा कि पैक्स ने कहा, आप शायद इससे ज्यादा तेज नहीं लेंगे। कारण यह है कि लगभग कोई फाइल सिस्टम नहीं है जो फ़ाइल की शुरुआत से ट्रंकटिंग का समर्थन करता है इसलिए यह एक ओ ( n) ऑपरेशन होने वाला है जहां nफ़ाइल का आकार है। यद्यपि आप बहुत तेजी से कर सकते हैं, लेकिन बाइट्स की एक ही संख्या (शायद रिक्त स्थान या एक टिप्पणी के साथ) के साथ पहली पंक्ति को अधिलेखित कर सकते हैं जो आपके लिए काम कर सकता है जो आप वास्तव में क्या करने की कोशिश कर रहे हैं (जिस तरह से है?)।


पुन : ... लगभग कोई फ़ाइल सिस्टम जो ट्रंकटिंग का समर्थन करता है ... " : यह दिलचस्प है; कृपया इस तरह के एक फाइलसिस्टम का नामकरण एक अभिभावकीय नोट सहित विचार करें।
एग्रिक

1
@agc: अब अप्रासंगिक है, लेकिन 70 के दशक में मेरा पहला काम क्वैडेक्स के साथ था, एक छोटा सा स्टार्टअप (अब चला गया, और उस नाम का उपयोग करके अब दोनों कंपनियों से असंबंधित)। उनके पास एक फाइलसिस्टम था, जो किसी फाइल के शुरुआत या अंत में जोड़ने या हटाने की अनुमति देता था , जिसका उपयोग ज्यादातर 3KB से कम की फाइल में ऊपर-विंडो और नीचे-विंडो को डालकर संपादन को लागू करने के लिए किया जाता था। इसका खुद का कोई नाम नहीं था, यह क्यूएमओएस, क्वैडेक्स मल्टीएयर ऑपरेटिंग सिस्टम का सिर्फ एक हिस्सा था। ('मल्टी' आमतौर पर एक LSI-11/02 पर 64KB रैम के साथ 2-3 था और आमतौर पर कुछ RX01- टाइप 8 "फ्लॉपी प्रत्येक 250KB डिस्क के साथ।) :-)
dave_thompson_085

9

spongeUtil एक अस्थायी फ़ाइल से खेल के लिए जरूरत से बचा जाता है:

tail -n +2 "$FILE" | sponge "$FILE"

spongeवास्तव में स्वीकृत समाधान ( tail -n +2 "$FILE" > "$FILE.tmp" && mv "$FILE.tmp" "$FILE") से अधिक क्लीनर और अधिक मजबूत है
Jealie

1
यह स्पष्ट किया जाना चाहिए कि 'स्पंज' के लिए 'मोरटेल' पैकेज की आवश्यकता होती है।
FedFranzoni

यह एकमात्र समाधान है जिसने मेरे लिए एक सिस्टम फ़ाइल (एक डेबियन डोकर छवि पर) को बदलने के लिए काम किया। फ़ाइल लिखने का प्रयास करते समय "डिवाइस या संसाधन व्यस्त" त्रुटि के कारण अन्य समाधान विफल रहे।
FedFranzoni

लेकिन क्या spongeपूरी फाइल को मेमोरी में बफर करता है ? अगर यह सैकड़ों GB का है तो यह काम नहीं करेगा।
ऑरेंजडॉग

@ ऑरेंजडॉग, इसलिए जब तक फाइल सिस्टम इसे स्टोर कर सकता है, तब तक इसे spongeसोख कर रख सकते हैं , क्योंकि यह एक / tmp फाइल को इंटरमीडिएट स्टेप के रूप में उपयोग करता है , जिसे बाद में मूल बाद में बदलने के लिए उपयोग किया जाता है।
अगस्त ३२

8

आप जगह में फ़ाइल को संशोधित करना चाहते हैं, तो आप हमेशा मूल इस्तेमाल कर सकते हैं edअपने के बजाय रों treaming उत्तराधिकारी sed:

ed "$FILE" <<<$'1d\nwq\n'

edआदेश, मूल यूनिक्स पाठ संपादक से पहले वहाँ भी पूर्ण स्क्रीन टर्मिनलों थे, बहुत कम चित्रमय वर्कस्टेशन। exसंपादक, सबसे अच्छा उपयोग कर रहे हैं क्या जब टाइपिंग पेट के शीघ्र में कम से रूप में जाना जाता viहै, एक है पूर्व की जाती थी संस्करण ed, एक ही आदेशों काम के इतने। जबकि edइसका उपयोग अंतःक्रियात्मक रूप से करने के लिए किया जाता है, इसका उपयोग बैच मोड में भी किया जा सकता है, इसके लिए एक स्ट्रिंग भेजकर, जो कि यह समाधान करता है।

अनुक्रम <<<$'1d\nwq\n'लाभ बैश के समर्थन के यहाँ-तार (के लिए ले जाता है <<<) और POSIX उद्धरण ( $'... 'फ़ीड इनपुट करने के लिए करने के लिए) ed: दो पंक्तियों से मिलकर आदेश 1dहै, जो eletes लाइन 1 , और फिर wq, जो डब्ल्यू संस्कार फ़ाइल वापस करने के लिए बाहर डिस्क और फिर q संपादन सत्र का उपयोग करता है।


यह सुरुचिपूर्ण है। +1
अर्मिन

लेकिन आपको पूरी फाइल को मेमोरी में पढ़ना होगा, जो सैकड़ों जीबी होने पर काम नहीं करेगा।
ऑरेंजडॉग

5

पहली पंक्ति को छोड़कर लाइनों को दिखाना चाहिए:

cat textfile.txt | tail -n +2

4
- आप
जोर से

5
@niglesiais मैं "बिल्ली के बेकार उपयोग" से असहमत हूं, क्योंकि यह स्पष्ट करता है कि यह समाधान पाइप की गई सामग्री पर ठीक है और न केवल फाइलें।
तितौ

5

ऐसा करने के लिए विम का उपयोग कर सकते हैं:

vim -u NONE +'1d' +'wq!' /tmp/test.txt

यह तेजी से होना चाहिए, क्योंकि vim प्रक्रिया के दौरान पूरी फ़ाइल नहीं पढ़ेगा।


+wq!अगर आपके खोल को कोसना है तो उद्धृत करने की आवश्यकता हो सकती है। शायद नहीं क्योंकि !एक शब्द की शुरुआत में नहीं है, लेकिन चीजों को उद्धृत करने की आदत में हो रही है शायद चारों ओर अच्छा है। (और यदि आप अनावश्यक रूप से उद्धृत न करके सुपर-दक्षता के लिए जा रहे हैं, तो आपको आसपास के उद्धरणों की आवश्यकता नहीं है 1d।)
मार्क रीड

vim है पूरी फ़ाइल पढ़ने की जरूरत है। वास्तव में यदि फ़ाइल मेमोरी से बड़ी है, जैसा कि इस क्यू में पूछा गया है, विम पूरी फाइल को पढ़ता है और इसे (या अधिकतर इसे) एक टेम्प फाइल में लिखता है, और संपादित करने के बाद यह सब वापस (स्थायी फ़ाइल में) लिखता है। मुझे नहीं पता कि आपको कैसे लगता है कि यह संभवतः इसके बिना काम कर सकता है ।
dave_thompson_085

4

Csplit का उपयोग कैसे करें?

man csplit
csplit -k file 1 '{1}'

यह सिंटैक्स भी काम करेगा, लेकिन केवल तीन के बजाय दो आउटपुट फाइलें उत्पन्न करता है csplit file /^.*$/1:। या अधिक बस csplit file //1:। या इससे भी अधिक सरल csplit file 2:।
मार्को रॉय

1

चूंकि ऐसा लगता है कि मैं विलोपन को गति नहीं दे सकता, इसलिए मुझे लगता है कि इस तरह से बैचों में फ़ाइल को संसाधित करने के लिए एक अच्छा तरीका हो सकता है:

While file1 not empty
  file2 = head -n1000 file1
  process file2
  sed -i -e "1000d" file1
end

इसका दोष यह है कि अगर प्रोग्राम बीच में ही मार जाता है (या अगर वहां कुछ खराब सीक्वल है - जिससे "प्रक्रिया" भाग मरने या लॉक-अप करने का कारण बनता है), ऐसी लाइनें होंगी जो या तो छोड़ दी जाती हैं, या दो बार संसाधित होती हैं ।

(file1 में sql कोड की लाइनें शामिल हैं)


पहली पंक्ति में क्या होता है? जैसा कि मैंने अपनी पोस्ट में सुझाया था, क्या आप इसे एक sql टिप्पणी के साथ लिख सकते हैं?
रॉबर्ट जुआ

0

यदि आप जो करना चाहते हैं, वह असफलता के बाद ठीक हो जाता है, तो आप बस एक फ़ाइल का निर्माण कर सकते हैं जो आपने अभी तक किया है।

if [[ -f $tmpf ]] ; then
    rm -f $tmpf
fi
cat $srcf |
    while read line ; do
        # process line
        echo "$line" >> $tmpf
    done

0

यह एक लाइनर करेगा:

echo "$(tail -n +2 "$FILE")" > "$FILE"

यह काम करता है, चूंकि फ़ाइल tailको पहले निष्पादित किया गया है echoऔर फिर फ़ाइल को अनलॉक किया गया है, इसलिए किसी अस्थायी फ़ाइल की आवश्यकता नहीं है।


-1

क्या N-1 लाइनों पर पूंछ का उपयोग करना और एक फ़ाइल में निर्देश देना, इसके बाद पुरानी फ़ाइल को हटाकर नई फ़ाइल को पुराने नाम पर फिर से काम करना होगा?

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


पहला समाधान अनिवार्य रूप से समान है कि ब्रेंट अब कर रहा है। मैं आपके प्रोग्रामेटिक दृष्टिकोण को नहीं समझता, केवल पहली पंक्ति को हटाने की आवश्यकता है, आप बस पहली पंक्ति को पढ़ेंगे और छोड़ देंगे और बाकी को किसी अन्य फ़ाइल में कॉपी कर लेंगे जो फिर से उसी तरह है जैसे कि sed और tail approach।
रॉबर्ट गैम्बल

दूसरे समाधान में निहितार्थ है कि फ़ाइल हर बार पहली पंक्ति से सिकुड़ी नहीं है। कार्यक्रम बस इसे संसाधित करता है, जैसे कि यह सिकुड़ गया था, लेकिन अगली बार हर बार शुरू होता है
EvilTeach

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