फ़ाइल में पहली पंक्ति को हटाने के लिए कौन सा तेज़ है ... sed या टेल?


14

इस उत्तर में ( मैं फ़ाइल की पहली पंक्ति को सेड के साथ कैसे हटा सकता हूँ? ) फ़ाइल में पहला रिकॉर्ड हटाने के दो तरीके हैं:

sed '1d' $file >> headerless.txt

** ---------------- या ---------------- **

tail -n +2 $file >> headerless.txt

व्यक्तिगत रूप से मुझे लगता है कि tailविकल्प कॉस्मैटिक रूप से अधिक सुखदायक और अधिक पठनीय है लेकिन शायद इसलिए क्योंकि मैं सेड-चैलेंजेड हूं।

कौन सा तरीका सबसे तेज़ है?


5
उत्तर नहीं, लेकिन एक संभावित विचार यह है कि उबंटू पर ठीक काम करने के sedलिए अधिक पोर्टेबल: "+2" है tail, जो GNU का उपयोग करता है tail, लेकिन BSD पर काम नहीं करेगा tail
जॉन एन

@JNN tailने क्रॉस-प्लेटफॉर्म कॉम्पेटिबिलिटी की कमी को साझा करने के लिए धन्यवाद ।
विनयुनुच्स

3
@ जॉन एन "+2" टेल के लिए ठीक काम करता है, मैक सियरा चल रहा है जो बीएसडी टेल कमांड का उपयोग करने का दावा करता है
निक सिलिटो

उरग, आप काफी सही हैं - मैंने अभी इसे फिर से चलाया है और इस बार इनपुट की जाँच की। जो मुझे पहली बार करना चाहिए था। यह POSIX भी है। / बंद हो जाता है, गले लगा लिया।
जॉन एन

2
@ जॉन आप पूरी तरह से गलत नहीं हैं। अतीत में, UNIX ने -nविकल्प प्रदान नहीं किया , और वाक्यविन्यास का उपयोग किया tail +2 $fileFreebsd.org/cgi/… देखें यह संभव है कि आप आधुनिक बीएसडी में से एक के बजाय उसी के बारे में सोच रहे थे।
hvd

जवाबों:


28

किसी फ़ाइल की पहली पंक्ति को हटाने के लिए sedबनाम tailका प्रदर्शन

टी एल; डॉ

  • sed बहुत शक्तिशाली और बहुमुखी है, लेकिन यह वह है जो इसे धीमा करता है, विशेष रूप से कई लाइनों के साथ बड़ी फ़ाइलों के लिए।

  • tail सिर्फ एक साधारण बात करता है, लेकिन यह एक अच्छी तरह से और तेजी से करता है, यहां तक ​​कि कई लाइनों के साथ बड़ी फ़ाइलों के लिए भी।

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

प्रयोग

सामान्य तैयारी:

विश्लेषण करने के लिए हमारे आदेश हैं:

sed '1d' testfile > /dev/null
tail -n +2 testfile > /dev/null

ध्यान दें कि मैं /dev/nullटर्मिनल आउटपुट को खत्म करने के लिए हर बार आउटपुट को पाइप कर रहा हूं या प्रदर्शन अड़चन के रूप में फाइल लिखता हूं ।

आइए डिस्क I / O को संभावित अड़चन के रूप में समाप्त करने के लिए एक रैम डिस्क सेट करें। मेरे पास व्यक्तिगत रूप से एक tmpfsघुड़सवार है /tmpइसलिए मैंने बस testfileइस प्रयोग के लिए वहां रखा ।

फिर मैं एक बार एक यादृच्छिक परीक्षण फ़ाइल बना रहा हूं जिसमें इस $numoflinesलाइन का उपयोग करके यादृच्छिक लाइन की लंबाई और यादृच्छिक डेटा के साथ निर्दिष्ट मात्रा है (ध्यान दें कि यह निश्चित रूप से इष्टतम नहीं है, यह लगभग> 2M लाइनों के लिए धीमा हो जाता है, लेकिन कौन परवाह करता है, यह नहीं है बात हम विश्लेषण कर रहे हैं):

cat /dev/urandom | base64 -w0 | tr 'n' '\n'| head -n "$numoflines" > testfile

ओह, btw। मेरा टेस्ट लैपटॉप उबंटू 16.04, 64 बिट इंटेल आई 5-6200 यू सीपीयू पर चल रहा है। सिर्फ तुलना के लिए।

बड़ी फ़ाइलें समय:

एक विशाल की स्थापना testfile:

numoflines=10000000एक यादृच्छिक फ़ाइल का उत्पादन 10M लाइनों के साथ ऊपर से चल रहा है , 600 एमबी से अधिक पर कब्जा कर रहा है - यह काफी बड़ा है, लेकिन हम इसके साथ शुरू करते हैं, क्योंकि हम कर सकते हैं:

$ wc -l testfile 
10000000 testfile

$ du -h testfile 
611M    testfile

$ head -n 3 testfile 
qOWrzWppWJxx0e59o2uuvkrfjQbzos8Z0RWcCQPMGFPueRKqoy1mpgjHcSgtsRXLrZ8S4CU8w6O6pxkKa3JbJD7QNyiHb4o95TSKkdTBYs8uUOCRKPu6BbvG
NklpTCRzUgZK
O/lcQwmJXl1CGr5vQAbpM7TRNkx6XusYrO

हमारे विशाल के साथ समयबद्ध प्रदर्शन करें testfile:

अब हम दोनों आदेशों के साथ केवल एक समयबद्ध रन बनाते हैं, यह अनुमान लगाने के लिए कि हम किस परिमाण के साथ काम कर रहे हैं।

$ time sed '1d' testfile > /dev/null
real    0m2.104s
user    0m1.944s
sys     0m0.156s

$ time tail -n +2 testfile > /dev/null
real    0m0.181s
user    0m0.044s
sys     0m0.132s

हम पहले से ही बड़ी फ़ाइलों के लिए वास्तव में स्पष्ट परिणाम देखते हैं, tailकी तुलना में तेजी से एक परिमाण है sed। लेकिन सिर्फ मज़े के लिए और यह सुनिश्चित करने के लिए कि कोई बड़ा साइड इफ़ेक्ट नहीं है, कोई फर्क नहीं पड़ता, चलो इसे 100 बार करें:

$ time for i in {1..100}; do sed '1d' testfile > /dev/null; done
real    3m36.756s
user    3m19.756s
sys     0m15.792s

$ time for i in {1..100}; do tail -n +2 testfile > /dev/null; done
real    0m14.573s
user    0m1.876s
sys     0m12.420s

निष्कर्ष एक ही रहता है, sedएक बड़ी फ़ाइल की पहली पंक्ति को हटाने के लिए अक्षम है, tailवहां उपयोग किया जाना चाहिए।

और हाँ, मुझे पता है कि बैश के लूप निर्माण धीमी गति से होते हैं, लेकिन हम यहां केवल कुछ पुनरावृत्तियों कर रहे हैं और एक सादा लूप लेने में वैसे भी sed/ tailruntimes की तुलना में महत्वपूर्ण नहीं है।

छोटी फ़ाइलें समय:

एक छोटी सी स्थापना testfile:

अब पूर्णता के लिए, आइए अधिक सामान्य मामले को देखें कि आपके पास kB रेंज में एक छोटी इनपुट फ़ाइल है। आइए numoflines=100इस तरह की खोज के साथ एक यादृच्छिक इनपुट फ़ाइल बनाएँ :

$ wc -l testfile 
100 testfile

$ du -h testfile 
8,0K    testfile

$ head -n 3 testfile 
tYMWxhi7GqV0DjWd
pemd0y3NgfBK4G4ho/
aItY/8crld2tZvsU5ly

हमारे छोटे से समयबद्ध रन का प्रदर्शन करें testfile:

जैसा कि हम अनुभव से कुछ मिलीसेकंड की सीमा में ऐसी छोटी फ़ाइलों के लिए समय की उम्मीद कर सकते हैं, चलो अभी 1000 पुनरावृत्तियों करते हैं:

$ time for i in {1..1000}; do sed '1d' testfile > /dev/null; done
real    0m7.811s
user    0m0.412s
sys     0m7.020s

$ time for i in {1..1000}; do tail -n +2 testfile > /dev/null; done
real    0m7.485s
user    0m0.292s
sys     0m6.020s

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


+1 जवाब देने के लिए धन्यवाद। मैंने सर्ग की टिप्पणी के आधार पर मूल प्रश्न (खेद) को संपादित किया जो awkऐसा भी कर सकता है। मेरा मूल प्रश्न उस लिंक पर आधारित था जो मुझे पहली बार मिला था। अपनी सारी मेहनत के बाद कृपया सलाह दें कि क्या मुझे awkएक समाधान उम्मीदवार के रूप में हटा देना चाहिए और केवल sedऔर केवल मूल परियोजना के दायरे पर ध्यान केंद्रित करना चाहिए tail
विनयुनुच्स

यह कैसी व्यवस्था है? मेरे मैक (इसलिए बीएसडी टूल्स) पर, / usr / शेयर / तानाशाह / शब्दों पर परीक्षण मुझे sed के लिए 0.09s और पूंछ के लिए 0.19s (और awk 'NR > 1', दिलचस्प)।
केविन

5

यहाँ एक और विकल्प है, बस बैश बिल्डिंग्स का उपयोग करके और cat:

{ read ; cat > headerless.txt; } < $file

$file{ }आदेश समूहीकरण में पुनर्निर्देशित किया जाता है । readबस पढ़ता है और पहली पंक्ति को छोड़ देता है। शेष स्ट्रीम को तब पाइप किया जाता है, catजो इसे गंतव्य फ़ाइल पर लिखता है।

मेरे Ubuntu 16.04 पर इस और tailसमाधान का प्रदर्शन बहुत समान है। मैंने एक लार्जिश टेस्ट फाइल बनाई है जिसमें seq:

$ seq 100000000 > 100M.txt
$ ls -l 100M.txt 
-rw-rw-r-- 1 ubuntu ubuntu 888888898 Dec 20 17:04 100M.txt
$

tail समाधान:

$ time tail -n +2 100M.txt > headerless.txt

real    0m1.469s
user    0m0.052s
sys 0m0.784s
$ 

cat/ ब्रेस समाधान:

$ time { read ; cat > headerless.txt; } < 100M.txt 

real    0m1.877s
user    0m0.000s
sys 0m0.736s
$ 

मेरे पास अभी एक उबंटू वीएम काम है, हालांकि, और दोनों के समय में महत्वपूर्ण भिन्नता देखी गई है, हालांकि वे सभी एक ही बॉलपार्क में हैं।


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

@ WinEunuuchs2Unix टाइमिंग जोड़ी, हालांकि वे बहुत विश्वसनीय नहीं हैं क्योंकि यह एक वीएम पर है। मेरे पास अभी एक नंगे धातु उबंटू स्थापना काम नहीं है।
डिजिटल ट्रामा

मुझे नहीं लगता कि जब आप वीएम की तुलना वीएम से कर रहे हैं, तो वीएम बनाम नंगे धातु के मामले। समय प्रमाण के लिए धन्यवाद। मैं शायद साथ जाऊंगा, tailलेकिन अभी भी लगता है कि readविकल्प बहुत अच्छा है।
विनयुनुच्स 2 यूनिक्स

4

मेरे सिस्टम पर कोशिश कर रहा है, और प्रत्येक कमांड को उपसर्ग कर timeरहा हूं जिसके साथ मुझे निम्नलिखित परिणाम मिले:

sed:

real    0m0.129s
user    0m0.012s
sys     0m0.000s

और पूंछ:

real    0m0.003s
user    0m0.000s
sys     0m0.000s

जो सुझाव देता है कि, मेरे सिस्टम पर कम से कम AMD FX 8250 Ubuntu 16.04 चल रहा है, पूंछ काफी तेज है। परीक्षण फ़ाइल में 540k के आकार के साथ 10,000 लाइनें थीं। फाइल को एचडीडी से पढ़ा गया था।


+1 जवाब देने के लिए धन्यवाद। एयू टोकन में एक अलग परीक्षण में एक उपयोगकर्ता ने दिखाया कि 61 एमबी फ़ाइल के साथ रैमडिस्क का उपयोग करके sed (21.86 सेकंड) की तुलना में 10 गुना तेज (2.31 सेकंड) है। मैंने आपके जवाब को कोड ब्लॉक लागू करने के लिए संपादित किया था, लेकिन आप इसे अपने द्वारा उपयोग किए गए फ़ाइल आकार के साथ भी संपादित करना चाह सकते हैं।
विनयुनुच्स

@ पूरी तरह से उचित है कि यह केवल एक महत्वपूर्ण उत्तर है, और संभवतः आपको अलग-अलग हार्डवेयर कॉन्फ़िगरेशन, विभिन्न परीक्षण फ़ाइलों आदि के साथ अलग-अलग परिणाम
मिलेंगे

2
फ़ाइल कैश में नहीं है, जब sedइस परिणाम में एक कारक खेल सकते हैं , तो यह है कि आप उन्हें परीक्षण किया है।
Minix

किस प्रकार की प्रणाली? जैसा कि मैंने यहां एक और पोस्ट पर टिप्पणी की, मेरे मैक sedपर लगभग दो बार उपवास किया गया था।
केविन

1

कहने का कोई वस्तुनिष्ठ तरीका नहीं है जो बेहतर है, क्योंकि sedऔर tailकेवल वही चीजें हैं जो प्रोग्राम निष्पादन के दौरान सिस्टम पर चलती हैं। बहुत सारे कारक जैसे डिस्क i / o, नेटवर्क i / o, CPU उच्च प्राथमिकता प्रक्रियाओं के लिए बाधित होता है - वे सभी प्रभाव जो आपके प्रोग्राम को कितनी तेजी से चलाएंगे।

दोनों सी में लिखे गए हैं, इसलिए यह भाषा का मुद्दा नहीं है, लेकिन पर्यावरण के अधिक है। उदाहरण के लिए, मेरे पास एसएसडी है और मेरे सिस्टम पर यह माइक्रोसेकंड में समय लगेगा, लेकिन हार्ड ड्राइव पर एक ही फ़ाइल के लिए अधिक समय लगेगा क्योंकि एचडीडी काफी धीमे हैं। तो हार्डवेयर भी इसमें भूमिका निभाता है।

कुछ चीजें हैं जिन्हें आप ध्यान में रखना चाह सकते हैं कि किस कमांड को चुनना है:

  • तुम्हारा उद्देश्य क्या है ? sedपाठ को बदलने के लिए स्ट्रीम संपादक है।tailपाठ की विशिष्ट लाइनों के उत्पादन के लिए है। यदि आप लाइनों से निपटना चाहते हैं और केवल उन्हें प्रिंट करना चाहते हैं, तो उपयोग करें tail। यदि आप पाठ को संपादित करना चाहते हैं, तो उपयोग करें sed
  • tailकी तुलना में कहीं अधिक सरल वाक्यविन्यास है sed, इसलिए जो आप स्वयं पढ़ सकते हैं और जो अन्य पढ़ सकते हैं, उसका उपयोग करें।

एक अन्य महत्वपूर्ण कारक आपके द्वारा संसाधित किए जा रहे डेटा की मात्रा है। छोटी फाइलें आपको कोई प्रदर्शन अंतर नहीं देंगी। जब आप बड़ी फ़ाइलों के साथ काम कर रहे हों तो तस्वीर दिलचस्प हो जाती है। एक 2 जीबी BIGFILE.txt के साथ, हम देख सकते हैं कि sedसिस्टम कॉल की तुलना में कहीं अधिक है tail, और काफी धीमी गति से चलता है।

bash-4.3$ du -sh BIGFILE.txt 
2.0G    BIGFILE.txt
bash-4.3$ strace -c  sed '1d' ./BIGFILE.txt  > /dev/null
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 59.38    0.079781           0    517051           read
 40.62    0.054570           0    517042           write
  0.00    0.000000           0        10         1 open
  0.00    0.000000           0        11           close
  0.00    0.000000           0        10           fstat
  0.00    0.000000           0        19           mmap
  0.00    0.000000           0        12           mprotect
  0.00    0.000000           0         1           munmap
  0.00    0.000000           0         3           brk
  0.00    0.000000           0         2           rt_sigaction
  0.00    0.000000           0         1           rt_sigprocmask
  0.00    0.000000           0         1         1 ioctl
  0.00    0.000000           0         7         7 access
  0.00    0.000000           0         1           execve
  0.00    0.000000           0         1           getrlimit
  0.00    0.000000           0         2         2 statfs
  0.00    0.000000           0         1           arch_prctl
  0.00    0.000000           0         1           set_tid_address
  0.00    0.000000           0         1           set_robust_list
------ ----------- ----------- --------- --------- ----------------
100.00    0.134351               1034177        11 total
bash-4.3$ strace -c  tail  -n +2 ./BIGFILE.txt  > /dev/null
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 62.30    0.148821           0    517042           write
 37.70    0.090044           0    258525           read
  0.00    0.000000           0         9         3 open
  0.00    0.000000           0         8           close
  0.00    0.000000           0         7           fstat
  0.00    0.000000           0        10           mmap
  0.00    0.000000           0         4           mprotect
  0.00    0.000000           0         1           munmap
  0.00    0.000000           0         3           brk
  0.00    0.000000           0         1         1 ioctl
  0.00    0.000000           0         3         3 access
  0.00    0.000000           0         1           execve
  0.00    0.000000           0         1           arch_prctl
------ ----------- ----------- --------- --------- ----------------
100.00    0.238865                775615         7 total

+1 जवाब देने के लिए धन्यवाद। लेकिन मुझे यकीन नहीं है कि यह टिप्पणी मुझे यह तय करने में मदद कर रही है कि मुझे किस कमांड का उपयोग करना चाहिए ....
WinEunuuchs2Unix

@ WinEunuuchs2Unix ठीक है, आपने पूछा कि कौन सी कमांड बेहतर है, इसलिए मैं उस प्रश्न का सटीक उत्तर दे रहा हूं। कौन सा कमांड चुनना है, आप पर निर्भर है। यदि आप इससे tailबेहतर पढ़ सकते हैं sed- तो इसका उपयोग करें। मैं व्यक्तिगत रूप से उपयोग करूंगा pythonया awkइसके बजाय sedक्योंकि यह जटिल हो सकता है। इसके अलावा, यदि आप प्रदर्शन के बारे में चिंतित हैं, तो वास्तविकता का सामना करें - आप यहां माइक्रोसेकंड में परिणाम देख रहे हैं। आप अंतर महसूस नहीं होगा जब तक कि यह गीगाबाइट है कि आप पढ़ने के लिए कोशिश कर रहे हैं की सीमा में एक freakin विशाल फ़ाइल है
सर्गी Kolodyazhnyy

ओह, मैं एक awkउत्तर की भी सराहना करूंगा:) ... मेरा प्रश्न एक अन्य एयू क्यू एंड ए (लिंक में) पर आधारित था और वहां उन्होंने कभी उल्लेख नहीं किया awk। मैं मानता हूं कि समय का अंतर छोटी फाइलों पर नाममात्र का है। मैं बस कुछ अच्छी आदतें विकसित करने की कोशिश कर रहा था।
विनयुनुच्स

1
@ WinEunuuchs2Unix ज़रूर, यहाँ यह है awk 'NR!=1' input_file.txt :। यह मुझे समान रूप से समान परिणाम देता है, लगभग 150 मिलीसेकंड, दोनों के लिए समान संख्या tailऔर sed। लेकिन एगियन, मैं एसएसडी का उपयोग कर रहा हूं, इसलिए मैं कहूंगा कि यह हार्ड ड्राइव और सीपीयू है जो कि बात है, कमांड नहीं।
सर्गी कोलोडाज़नी

1
@ केवल 1 एमबी लाइनों वाली 60 एमबी की फ़ाइल के साथ भी, sed3 मिनट से अधिक समय तक 1000 रन , जबकि tailकेवल 20 सेकंड की आवश्यकता होती है। यही कारण है कि नहीं है कि बड़ा अभी तक वास्तव में, निश्चित रूप से नहीं जीबी रेंज में।
बाइट कमांडर

1

शीर्ष उत्तर ने डिस्क को खाते में नहीं लिया > /dev/null

यदि आपके पास एक बड़ी फ़ाइल है और आप अपनी डिस्क पर अस्थायी डुप्लिकेट बनाना नहीं चाहते हैं vim -c

$ cat /dev/urandom | base64 -w0 | tr 'n' '\n'| head -n 10000000 > testfile
$ time sed -i '1d' testfile

real    0m59.053s
user    0m9.625s
sys     0m48.952s

$ cat /dev/urandom | base64 -w0 | tr 'n' '\n'| head -n 10000000 > testfile
$ time vim -e -s testfile -c ':1d' -c ':wq'

real    0m8.259s
user    0m3.640s
sys     0m3.093s

संपादित करें: यदि फ़ाइल उपलब्ध मेमोरी से बड़ी है, तो vim -cयह काम नहीं करता है, ऐसा लगता है कि यह फ़ाइल के एक वृद्धिशील भार को करने के लिए पर्याप्त स्मार्ट नहीं है।


0

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

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