किसी फ़ाइल से nth लाइन प्राप्त करने के लिए टूल को बैश करें


603

क्या ऐसा करने का एक "विहित" तरीका है? मैं प्रयोग कर रहा हूँ head -n | tail -1जो चाल करता है, लेकिन मैं सोच रहा था कि क्या कोई बैश टूल है जो विशेष रूप से एक फ़ाइल से एक लाइन (या लाइनों की एक सीमा) निकालता है।

"विहित" से मेरा मतलब है कि एक कार्यक्रम जिसका मुख्य कार्य वह कर रहा है।


10
"यूनिक्स तरीका" श्रृंखला साधनों के लिए है जो अपने संबंधित कार्य को अच्छी तरह से करते हैं। इसलिए मुझे लगता है कि आपको पहले से ही एक बहुत ही उपयुक्त तरीका मिल गया है। अन्य विधियों में शामिल हैं awkऔर sedमुझे यकीन है कि कोई पर्ल वन-लाइनर या साथ ही आ सकता है;)
0xC0000022L

3
डबल-कमांड बताता है कि head | tailसमाधान उप-इष्टतम है। अन्य अधिक लगभग इष्टतम समाधान सुझाए गए हैं।
जोनाथन लेफ़लर

क्या आपने कोई बेंचमार्क चलाया है, जिस पर औसत केस के लिए समाधान सबसे तेज़ है?
मार्सिन

5
यूनिक्स और लिनक्स पर एक बड़ी फ़ाइल पर Y को लाइन करने के लिए बिल्ली लाइन X पर बेंचमार्क (एक सीमा के लिए) । (cc @Marcin, यदि आप दो साल बाद भी सोच रहे हैं)
केविन

6
head | tailसमाधान, काम नहीं करता है आप एक लाइन है कि इनपुट में मौजूद नहीं है क्वेरी: यह अंतिम पंक्ति प्रिंट होगा।
जर्नो

जवाबों:


800

headऔर पाइप tailएक बड़ी फ़ाइल के लिए धीमा होगा। मैं sedइस तरह का सुझाव दूंगा :

sed 'NUMq;d' file

NUMउस पंक्ति की संख्या कहां है जिसे आप प्रिंट करना चाहते हैं; इसलिए, उदाहरण के लिए, sed '10q;d' file10 वीं पंक्ति को प्रिंट करने के लिए file

स्पष्टीकरण:

NUMq लाइन नंबर होने पर तुरंत छोड़ दिया जाएगा NUM

dमुद्रण के बजाय लाइन को हटा देगा; यह अंतिम लाइन पर हिचक रहा है क्योंकिq छोड़ने के समय बाकी स्क्रिप्ट को छोड़ दिया जाता है।

यदि आपके पास NUMएक चर है, तो आप एकल के बजाय दोहरे उद्धरण चिह्नों का उपयोग करना चाहेंगे:

sed "${NUM}q;d" file

44
सोच उन लोगों के लिए, इस समाधान 9 के बारे में 6 गुना से अधिक तेजी से लगता है sed -n 'NUMp'और sed 'NUM!d'समाधान नीचे का प्रस्ताव रखा।
स्किप्पी ले ग्रांड गौरौ

75
मुझे लगता tail -n+NUM file | head -n1है कि बस तेज या तेज होने की संभावना है। कम से कम, यह (मेरे सिस्टम पर) काफी तेज था जब मैंने इसे NUM के साथ 250000 होने के साथ फाइल पर आधे मिलियन लाइनों के साथ आज़माया। YMMV, लेकिन मैं वास्तव में नहीं देखता कि यह क्यों होगा।
रिची

2
@rici (पहले की टिप्पणी का संशोधन) लिनक्स पर (उबंटू 12.04, फेडोरा 20), catवास्तव में तेज (लगभग दो बार तेज) का उपयोग कर रहा है , लेकिन केवल अगर फ़ाइल अभी तक कैश नहीं की गई हैफ़ाइल के कैश होने के बाद, फ़ाइल नाम तर्क का प्रत्यक्ष उपयोग तेज़ (लगभग 1/3 तेज़) होता है, जबकि catप्रदर्शन समान रहता है। उत्सुकता से, OS X 10.9.3 पर यह कोई भी फर्क नहीं पड़ता है: cat/ नहीं cat, फ़ाइल कैश की गई है या नहीं। @anubhava: मेरी खुशी।
mklement0

2
@SkippyleGrandGourou: इस अनुकूलन की विशिष्ट प्रकृति को देखते हुए , यहां तक ​​कि आपकी संख्याओं की सीमा भी सामान्य विवरण के रूप में निरर्थक है । एकमात्र सामान्य टेकअवे यह है: (ए) इस अनुकूलन को सुरक्षित रूप से सभी इनपुट पर लागू किया जा सकता है, (बी) प्रभाव समग्र रूप से संख्याओं के सापेक्ष मांगे गए रेखा के सूचकांक के आधार पर, नाटकीय से कोई भी नहीं होगा
mklement0

17
sed 'NUMqपहली NUMफ़ाइलों का उत्पादन करेगा और ;dसभी लेकिन अंतिम पंक्ति को हटा देगा।
अनुभा

304
sed -n '2p' < file.txt

2 लाइन प्रिंट करेगा

sed -n '2011p' < file.txt

2011 की लाइन

sed -n '10,33p' < file.txt

लाइन 33 तक लाइन 10

sed -n '1p;3p' < file.txt

पहली और 3 वीं पंक्ति

और इसी तरह...

Sed के साथ लाइनें जोड़ने के लिए, आप यह जाँच सकते हैं:

sed: एक निश्चित स्थिति में एक लाइन डालें


6
<इस मामले में @RafaelBarbosa जरूरी नहीं है। बस, पुनर्निर्देश का उपयोग करना मेरी प्राथमिकता है, क्योंकि मैं अक्सर पुनर्निर्देश का उपयोग करता था जैसे sed -n '100p' < <(some_command)- तो, ​​सार्वभौमिक वाक्यविन्यास :)। यह कम प्रभावी नहीं है, क्योंकि पुनर्निर्देशन शेल के साथ किया जाता है जब खुद को फोर्क करना होता है, तो ... यह केवल एक प्राथमिकता है ... (और हाँ, यह एक वर्ण लंबा है) :)
jm666

1
@ jm666 वास्तव में यह 2 वर्णों से अधिक लंबा है क्योंकि आप सामान्य रूप से '<' के साथ-साथ एक अतिरिक्त स्थान '' डालेंगे <के बाद <केवल एक स्थान पर लागू होने पर यदि आपने <:) का उपयोग नहीं किया है
rasen58

2
@ rasen58 अंतरिक्ष एक चरित्र भी है? :) / ठीक है, बस मजाक कर रहे हैं - आप सही / :)
jm666

1
@duimeime निश्चित रूप से, अगर किसी को अनुकूलन करने की आवश्यकता है। लेकिन "आम" समस्याओं के लिए IMHO ठीक है और अंतर ध्यान देने योग्य नहीं है। इसके अलावा, head/ परिदृश्य को tailहल नहीं करता sed -n '1p;3p'है - उर्फ ​​अधिक गैर-आसन्न पंक्तियों को प्रिंट करें ...
jm666

1
@ निश्चित रूप से - नोट सही और आवश्यक है। :)
jm666

93

मेरे पास एक अनूठी स्थिति है जहां मैं इस पृष्ठ पर प्रस्तावित समाधानों को बेंचमार्क कर सकता हूं, और इसलिए मैं इस जवाब को प्रस्तावित समाधानों के समेकन के रूप में लिख रहा हूं, जिनमें प्रत्येक के लिए रन टाइम शामिल है।

सेट अप

मेरे पास 3.261 गीगाबाइट एएससीआईआई पाठ डेटा फ़ाइल है जिसमें प्रति पंक्ति एक कुंजी-मूल्य जोड़ी है। फ़ाइल में कुल 3,339,550,320 पंक्तियाँ हैं और मेरे द्वारा आजमाए गए किसी भी संपादक को खोलने की कोशिश में ख़राबियाँ हैं। मुझे इस फाइल को सब्मिट करने की जरूरत है ताकि कुछ मूल्यों की जांच कर सकूं कि मैंने केवल पंक्ति ~ 500,000,000 के आसपास शुरू की है।

क्योंकि फ़ाइल में बहुत सारी पंक्तियाँ हैं:

  • मुझे डेटा के साथ कुछ भी उपयोगी करने के लिए पंक्तियों के केवल सबसेट को निकालने की आवश्यकता है।
  • जिन मूल्यों की मुझे परवाह है, उनके लिए हर पंक्ति को पढ़ना एक लंबा समय लेने वाला है।
  • यदि समाधान उन पंक्तियों को पढ़ता है जिनकी मुझे परवाह है और शेष फाइल को पढ़ना जारी रखता है तो इससे लगभग 3 बिलियन अप्रासंगिक पंक्तियों को पढ़ने में समय बर्बाद होगा और आवश्यकता से 6x अधिक समय लगेगा।

मेरा सबसे अच्छा मामला परिदृश्य एक समाधान है जो फ़ाइल में से किसी एक पंक्ति को बिना किसी अन्य पंक्तियों को पढ़े फ़ाइल में से निकालता है, लेकिन मैं यह नहीं सोच सकता कि मैं इसे बैश में कैसे पूरा करूंगा।

अपनी पवित्रता के प्रयोजनों के लिए मैं पूर्ण 500,000,000 पंक्तियों को पढ़ने की कोशिश नहीं करने जा रहा हूँ जिनकी मुझे अपनी समस्या के लिए आवश्यकता होगी। इसके बजाय मैं 3,339,550,320 में से 50,000,000 पंक्ति निकालने की कोशिश करूँगा (जिसका अर्थ है कि पूरी फ़ाइल पढ़ने में 60x ज़रूरत से ज़्यादा समय लगेगा)।

मैं timeप्रत्येक कमांड में अंतर्निहित बेंचमार्क का उपयोग करूंगा ।

बेसलाइन

पहले देखते हैं कि कैसे head tailहल करें:

$ time head -50000000 myfile.ascii | tail -1
pgm_icnt = 0

real    1m15.321s

पंक्ति ५० मिलियन के लिए आधार रेखा ००: ०१: १५.३२१ है, अगर मैं सीधे ५०० मिलियन पंक्ति के लिए जाऊं तो यह शायद १२.५ मिनट होगा।

कट गया

मैं इस एक के बारे में संदिग्ध हूँ, लेकिन यह एक शॉट के लायक है:

$ time cut -f50000000 -d$'\n' myfile.ascii
pgm_icnt = 0

real    5m12.156s

इस एक ने 00: 05: 12.156 को चलाने के लिए लिया, जो बेसलाइन की तुलना में बहुत धीमा है! मुझे यकीन नहीं है कि क्या यह पूरी फाइल के माध्यम से पढ़ा गया है या बस रोकने से पहले 50 मिलियन तक लाइन में है, लेकिन इसकी परवाह किए बिना समस्या का एक व्यवहार्य समाधान नहीं लगता है।

AWK

मैंने केवल समाधान के साथ भाग लिया exitक्योंकि मैं पूरी फ़ाइल के चलने की प्रतीक्षा नहीं कर रहा था:

$ time awk 'NR == 50000000 {print; exit}' myfile.ascii
pgm_icnt = 0

real    1m16.583s

यह कोड 00: 01: 16.583 में चला, जो केवल ~ 1 सेकंड धीमा है, लेकिन फिर भी बेसलाइन पर सुधार नहीं हुआ है। इस दर पर अगर बाहर निकलने की आज्ञा को छोड़ दिया गया होता तो शायद पूरी फ़ाइल पढ़ने में लगभग ~ 76 मिनट लग जाते!

पर्ल

मैंने मौजूदा पर्ल समाधान भी चलाया:

$ time perl -wnl -e '$.== 50000000 && print && exit;' myfile.ascii
pgm_icnt = 0

real    1m13.146s

यह कोड 00: 01: 13.146 में चला, जो बेसलाइन की तुलना में ~ 2 सेकंड तेज है। अगर मैं इसे पूर्ण ५००,०००,००० पर चलाऊँ तो शायद १२ मिनट लगेंगे।

sed

बोर्ड पर शीर्ष उत्तर, यहाँ मेरा परिणाम है:

$ time sed "50000000q;d" myfile.ascii
pgm_icnt = 0

real    1m12.705s

यह कोड ००: ०१: १२. ,०५ में चला, जो कि आधार रेखा से ३ सेकंड तेज है, और पर्ल से ०.४ सेकंड अधिक तेज है। अगर मैं इसे पूरी 500,000,000 पंक्तियों पर चलाता तो शायद ~ 12 मिनट लग जाते।

mapfile

मेरे पास 3.1 बैश है और इसलिए मैपफाइल समाधान का परीक्षण नहीं कर सकता है।

निष्कर्ष

ऐसा लगता है, अधिकांश भाग के लिए, head tailसमाधान पर सुधार करना मुश्किल है । सबसे अच्छा sedसमाधान दक्षता में ~ 3% की वृद्धि प्रदान करता है।

(सूत्र के साथ गणना प्रतिशत % = (runtime/baseline - 1) * 100)

पंक्ति 50,000,000

  1. 00: 01: 12.705 (-00: 00: 02.616 = -3.47%) sed
  2. 00: 01: 13.146 (-00: 00: 02.175 = -2.89%) perl
  3. 00: 01: 15.321 (+00: 00: 00.000 = + 0.00%) head|tail
  4. 00: 01: 16.583 (+00: 00: 01.262 = + 1.68%) awk
  5. 00: 05: 12.156 (+00: 03: 56.835 = + 314.43%) cut

पंक्ति 500,000,000 रु

  1. 00: 12: 07.050 (-00: 00: 26.160) sed
  2. 00: 12: 11.460 (-00: 00: 21.750) perl
  3. 00: 12: 33.210 (+00: 00: 00.000) head|tail
  4. 00: 12: 45.830 (+00: 00: 12.620) awk
  5. 00: 52: 01.560 (+00: 40: 31.650) cut

पंक्ति 3,338,559,320

  1. 01: 20: 54.599 (-00: 03: 05.327) sed
  2. 01: 21: 24.045 (-00: 02: 25.227) perl
  3. 01: 23: 49.273 (+00: 00: 00.000) head|tail
  4. 01: 25: 13.548 (+00: 02: 35.735) awk
  5. 05: 47: 23.026 (+04: 24: 26.246) cut

4
मुझे आश्चर्य है कि कब तक पूरी फ़ाइल को / dev / null में पूरा नहीं किया जाएगा। (क्या होगा अगर यह केवल एक हार्ड डिस्क बेंचमार्क था?)
sanmai

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

51

इसके साथ awkयह बहुत तेज़ है:

awk 'NR == num_line' file

जब यह सत्य है, तो डिफ़ॉल्ट व्यवहार awkकिया जाता है {print $0}:।


वैकल्पिक संस्करण

यदि आपकी फ़ाइल बहुत बड़ी है, तो आप exitआवश्यक पंक्ति पढ़ने के बाद बेहतर होंगे । इस तरह आप CPU समय बचाते हैं उत्तर के अंत में समय तुलना देखें

awk 'NR == num_line {print; exit}' file

यदि आप एक bash वैरिएबल से लाइन नंबर देना चाहते हैं, जिसका आप उपयोग कर सकते हैं:

awk 'NR == n' n=$num file
awk -v n=$num 'NR == n' file   # equivalent

देखें कि कितना समय बचाकर उपयोग किया जाता है exit, विशेष रूप से यदि पंक्ति फ़ाइल के पहले भाग में होती है:

# Let's create a 10M lines file
for ((i=0; i<100000; i++)); do echo "bla bla"; done > 100Klines
for ((i=0; i<100; i++)); do cat 100Klines; done > 10Mlines

$ time awk 'NR == 1234567 {print}' 10Mlines
bla bla

real    0m1.303s
user    0m1.246s
sys 0m0.042s
$ time awk 'NR == 1234567 {print; exit}' 10Mlines
bla bla

real    0m0.198s
user    0m0.178s
sys 0m0.013s

इसलिए अंतर 0.198 बनाम 1.303 है, लगभग 6 गुना तेजी से।


यह विधि हमेशा धीमी होने वाली है क्योंकि awk क्षेत्र विभाजन करने का प्रयास करता है। क्षेत्र बंटवारे की भूमि के ऊपर कम किया जा सकताawk 'BEGIN{FS=RS}(NR == num_line) {print; exit}' file
kvantour

इस विधि में awk की असली शक्ति तब सामने आती है जब आप file1, n2 of file2, n3 या file3 ... की लाइन n1 को बदलना चाहते हैं awk 'FNR==n' n=10 file1 n=30 file2 n=60 file3। GNU awk के साथ इस का उपयोग करके किया जा सकता है awk 'FNR==n{print;nextfile}' n=10 file1 n=30 file2 n=60 file3
kvantour

@kvtour वास्तव में, GNU awk का नेक्स्टाइल ऐसी चीजों के लिए बहुत अच्छा है। FS=RSक्षेत्र विभाजन से कैसे बचा जाए?
फेडोरक्वी 'एसओ ने नुकसान पहुंचाना बंद'

1
FS=RSक्षेत्र बंटवारे से बचने नहीं है, लेकिन यह केवल $ 0 लोगों को पार्स करता है और केवल एक ही क्षेत्र प्रदान करती नहीं है क्योंकि वहाँ RSमें$0
kvantour

@ क्वेंटौर मैं कुछ परीक्षण कर रहा हूं FS=RSऔर समय पर अंतर नहीं देखा। मेरे बारे में क्या आप इसके बारे में एक सवाल पूछ रहे हैं ताकि आप विस्तार कर सकें? धन्यवाद!
फेडोरक्वी 'एसओ ने

29

मेरे परीक्षणों के अनुसार, प्रदर्शन और पठनीयता के संदर्भ में मेरी सिफारिश है:

tail -n+N | head -1

Nवह पंक्ति संख्या है जो आप चाहते हैं। उदाहरण के लिए, tail -n+7 input.txt | head -1फ़ाइल की 7 वीं पंक्ति को प्रिंट करेगा।

tail -n+Nलाइन से शुरू होने वाली सभी चीजों को प्रिंट करेगा N, और head -1एक लाइन के बाद इसे बंद कर देगा।


विकल्प head -N | tail -1शायद थोड़ा अधिक पठनीय है। उदाहरण के लिए, यह 7 वीं पंक्ति को मुद्रित करेगा:

head -7 input.txt | tail -1

जब प्रदर्शन की बात आती है, तो छोटे आकारों के लिए बहुत अंतर नहीं होता है, लेकिन tail | headजब फाइलें बड़ी हो जाती हैं, तो इसे (ऊपर से) बेहतर बनाया जाएगा ।

शीर्ष-मतदान sed 'NUMq;d'जानना दिलचस्प है, लेकिन मैं तर्क दूंगा कि यह सिर / पूंछ के समाधान की तुलना में कम लोगों द्वारा समझा जाएगा और यह पूंछ / सिर की तुलना में भी धीमा है।

मेरे परीक्षणों में, दोनों पूंछों / सिर संस्करणों ने बेहतर प्रदर्शन किया sed 'NUMq;d' लगातार करते हैं। यह अन्य बेंचमार्क के अनुरूप है जो पोस्ट किए गए थे। ऐसा मामला खोजना कठिन है जहां पूंछ / सिर वास्तव में खराब था। यह भी आश्चर्य की बात नहीं है, क्योंकि ये ऐसे ऑपरेशन हैं जो आप एक आधुनिक यूनिक्स प्रणाली में भारी अनुकूलित होने की उम्मीद करेंगे।

प्रदर्शन अंतर के बारे में एक विचार प्राप्त करने के लिए, ये एक बड़ी फ़ाइल (9.3G) के लिए मिलने वाली संख्या हैं:

  • tail -n+N | head -1: 3.7 सेकंड
  • head -N | tail -1: 4.6 सेकंड
  • sed Nq;d: 18.8 सेकेंड

परिणाम भिन्न हो सकती है, लेकिन प्रदर्शन head | tailऔर tail | headसामान्य रूप में, है, छोटे आदानों के लिए तुलनीय है, और sedएक महत्वपूर्ण कारक (5x चारों ओर या तो) द्वारा हमेशा धीमी है।

मेरे बेंचमार्क को पुन: पेश करने के लिए, आप निम्नलिखित प्रयास कर सकते हैं, लेकिन चेतावनी दी जाती है कि यह वर्तमान कार्यशील निर्देशिका में 9.3G फाइल बनाएगा:

#!/bin/bash
readonly file=tmp-input.txt
readonly size=1000000000
readonly pos=500000000
readonly retries=3

seq 1 $size > $file
echo "*** head -N | tail -1 ***"
for i in $(seq 1 $retries) ; do
    time head "-$pos" $file | tail -1
done
echo "-------------------------"
echo
echo "*** tail -n+N | head -1 ***"
echo

seq 1 $size > $file
ls -alhg $file
for i in $(seq 1 $retries) ; do
    time tail -n+$pos $file | head -1
done
echo "-------------------------"
echo
echo "*** sed Nq;d ***"
echo

seq 1 $size > $file
ls -alhg $file
for i in $(seq 1 $retries) ; do
    time sed $pos'q;d' $file
done
/bin/rm $file

यहाँ मेरी मशीन पर एक रन का आउटपुट है (SSD के साथ थिंकपैड X1 कार्बन और मेमोरी का 16G)। मैं मानता हूं कि फाइनल रन सब कुछ कैश से आएगा, डिस्क से नहीं:

*** head -N | tail -1 ***
500000000

real    0m9,800s
user    0m7,328s
sys     0m4,081s
500000000

real    0m4,231s
user    0m5,415s
sys     0m2,789s
500000000

real    0m4,636s
user    0m5,935s
sys     0m2,684s
-------------------------

*** tail -n+N | head -1 ***

-rw-r--r-- 1 phil 9,3G Jan 19 19:49 tmp-input.txt
500000000

real    0m6,452s
user    0m3,367s
sys     0m1,498s
500000000

real    0m3,890s
user    0m2,921s
sys     0m0,952s
500000000

real    0m3,763s
user    0m3,004s
sys     0m0,760s
-------------------------

*** sed Nq;d ***

-rw-r--r-- 1 phil 9,3G Jan 19 19:50 tmp-input.txt
500000000

real    0m23,675s
user    0m21,557s
sys     0m1,523s
500000000

real    0m20,328s
user    0m18,971s
sys     0m1,308s
500000000

real    0m19,835s
user    0m18,830s
sys     0m1,004s

1
क्या प्रदर्शन head | tailबनाम के बीच अलग है tail | head? या यह इस बात पर निर्भर करता है कि किस लाइन को प्रिंट किया जा रहा है (फाइल की शुरुआत बनाम फाइल का अंत)?
वारबैंक

1
@wisbucky मेरे पास कोई कठिन आंकड़े नहीं हैं, लेकिन पहले "हेड -1" के बाद पूंछ का उपयोग करने का एक नुकसान यह है कि आपको अग्रिम में कुल लंबाई जानने की आवश्यकता है। यदि आप इसे नहीं जानते हैं, तो आपको पहले इसे गिनना होगा, जो कि एक प्रदर्शन-वार नुकसान होगा। एक और नुकसान यह है कि यह उपयोग करने के लिए कम सहज है। उदाहरण के लिए, यदि आपके पास 1 से 10 की संख्या है और आप तीसरी पंक्ति प्राप्त करना चाहते हैं, तो आपको "टेल -8। हेडर -1" का उपयोग करना होगा। "हेड -3 | टेल -1" की तुलना में अधिक त्रुटि प्रवण है।
फिलिप क्लेन

क्षमा करें, मुझे स्पष्ट होने के लिए एक उदाहरण शामिल करना चाहिए। head -5 | tail -1बनाम tail -n+5 | head -1। वास्तव में, मुझे एक और जवाब मिला जिसने एक परीक्षण की तुलना की और पाया tail | headकि यह तेज है। stackoverflow.com/a/48189289
wisbucky

1
@wisbucky इसका उल्लेख करने के लिए धन्यवाद! मैंने कुछ परीक्षण किए और मुझे इस बात से सहमत होना पड़ा कि जो मैंने देखा था, उस रेखा की स्थिति से स्वतंत्र यह हमेशा थोड़ा तेज था। यह देखते हुए, मैंने अपना उत्तर बदल दिया है और यदि कोई इसे पुन: उत्पन्न करना चाहता है तो उस बेंचमार्क को भी शामिल किया है।
फिलिप क्लेन

27

वाह, सारी संभावनाएँ!

इसे इस्तेमाल करे:

sed -n "${lineNum}p" $file

या इनमें से एक आपके Awk के संस्करण पर निर्भर करता है:

awk  -vlineNum=$lineNum 'NR == lineNum {print $0}' $file
awk -v lineNum=4 '{if (NR == lineNum) {print $0}}' $file
awk '{if (NR == lineNum) {print $0}}' lineNum=$lineNum $file

( आपको nawkया gawkकमांड का प्रयास करना पड़ सकता है )।

क्या कोई उपकरण है जो केवल उस विशेष लाइन को प्रिंट करता है? मानक उपकरणों में से एक नहीं। हालांकि, sedसंभवतः उपयोग करने के लिए निकटतम और सरल है।



21

इस सवाल को बैश को टैग किया जा रहा है, यहां बैश (way4) करने का तरीका है: (स्किप) और (काउंट) विकल्प के mapfileसाथ उपयोग करें ।-s-n

यदि आपको किसी फ़ाइल की 42 वीं पंक्ति प्राप्त करने की आवश्यकता है file:

mapfile -s 41 -n 1 ary < file

इस बिंदु पर, आपके पास एक सरणी होगी aryजिसमें उन फ़ील्ड्स fileशामिल होंगी जिनमें (अनुगामी न्यूलाइन सहित), जहाँ हमने पहले 41 लाइनों ( -s 41) को छोड़ दिया है , और एक लाइन ( -n 1) को पढ़ने के बाद रुक गए हैं । तो यह वास्तव में 42 वीं पंक्ति है। इसे प्रिंट करने के लिए:

printf '%s' "${ary[0]}"

यदि आपको लाइनों की एक श्रृंखला की आवश्यकता है, तो सीमा 42-666 (सम्मिलित) कहें, और कहें कि आप स्वयं गणित नहीं करना चाहते हैं, और उन्हें stdout पर प्रिंट करें:

mapfile -s $((42-1)) -n $((666-42+1)) ary < file
printf '%s' "${ary[@]}"

यदि आपको इन पंक्तियों को भी संसाधित करने की आवश्यकता है, तो अनुगामी न्यूलाइन को स्टोर करना वास्तव में सुविधाजनक नहीं है। इस मामले में -tविकल्प (ट्रिम) का उपयोग करें :

mapfile -t -s $((42-1)) -n $((666-42+1)) ary < file
# do stuff
printf '%s\n' "${ary[@]}"

आपके पास एक कार्य हो सकता है:

print_file_range() {
    # $1-$2 is the range of file $3 to be printed to stdout
    local ary
    mapfile -s $(($1-1)) -n $(($2-$1+1)) ary < "$3"
    printf '%s' "${ary[@]}"
}

कोई बाहरी आज्ञा नहीं, केवल बैश भवन!


11

आप सेड प्रिंट का उपयोग भी कर सकते हैं और छोड़ सकते हैं:

sed -n '10{p;q;}' file   # print line 10

6
-nविकल्प, तो डिफ़ॉल्ट कार्यवाही हर पंक्ति मुद्रित करने के लिए अक्षम कर देता है निश्चित रूप से के रूप में आप आदमी पेज पर एक ही नज़र से पता चला है।
14

में जीएनयू sed सभी sedजवाब एक ही गति के बारे में कर रहे हैं। इसलिए ( जीएनयू के लिए sed ) यह सबसे अच्छा sedजवाब है, क्योंकि यह बड़ी फ़ाइलों और छोटी एनटी लाइन मूल्यों के लिए समय की बचत करेगा ।
18


6

बड़ी फ़ाइलों के लिए सबसे तेज़ समाधान हमेशा पूंछ होता है। सिर, बशर्ते कि दो दूरी:

  • फ़ाइल की शुरुआत से शुरू होने वाली रेखा तक। इसे कहते हैंS
  • अंतिम पंक्ति से फ़ाइल के अंत तक की दूरी। बनोE

जाने जाते हैं। फिर, हम इसका उपयोग कर सकते हैं:

mycount="$E"; (( E > S )) && mycount="+$S"
howmany="$(( endline - startline + 1 ))"
tail -n "$mycount"| head -n "$howmany"

howmany बस आवश्यक लाइनों की गिनती है।

Https://unix.stackexchange.com/a/216614/79743 में कुछ और विवरण


1
कृपया ( Sऔर Eबाइट्स, वर्ण, या लाइनें) की इकाइयों को स्पष्ट करें ।
एजीसी

6

उपरोक्त सभी उत्तर सीधे प्रश्न का उत्तर देते हैं। लेकिन यहाँ एक कम प्रत्यक्ष समाधान है लेकिन संभावित रूप से अधिक महत्वपूर्ण विचार है, विचार को भड़काने के लिए।

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

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

उदाहरण के लिए, आप नए वर्णों के लिए चरित्र पदों की एक सूची बना सकते हैं:

awk 'BEGIN{c=0;print(c)}{c+=length()+1;print(c+1)}' file.txt > file.idx

फिर साथ पढ़ें tail, जो वास्तव में seekसीधे फ़ाइल में उपयुक्त बिंदु पर है!

लाइन 1000 पाने के लिए:

tail -c +$(awk 'NR=1000' file.idx) file.txt | head -1
  • यह 2-बाइट / मल्टीबीट वर्णों के साथ काम नहीं कर सकता है, क्योंकि awk "चरित्र-जागरूक" है, लेकिन पूंछ नहीं है।
  • मैंने एक बड़ी फाइल के खिलाफ यह परीक्षण नहीं किया है।
  • इसका उत्तर भी देखें ।
  • वैकल्पिक रूप से - अपनी फ़ाइल को छोटी फ़ाइलों में विभाजित करें!

5

CaffeineConnoisseur के बहुत ही उपयोगी बेंचमार्किंग जवाब के फॉलोअप के रूप में ... मैं उत्सुक था कि 'मैपफाइल' विधि की तुलना में दूसरों की तुलना में कितनी तेजी से (जैसा कि परीक्षण नहीं किया गया था), इसलिए मैंने खुद के रूप में एक त्वरित-गंदी गति की तुलना करने की कोशिश की मेरे पास 4 बैश हैं। "पूंछ | हेड" विधि के एक परीक्षण में फेंक दिया गया (सिर के बजाय पूंछ।) शीर्ष उत्तर पर टिप्पणियों में से एक में उल्लेख किया गया था जब मैं उस पर था, जैसा कि लोग इसकी प्रशंसा गा रहे हैं। मेरे पास इस्तेमाल किए जाने वाले टेस्टफाइल के आकार के लगभग कुछ भी नहीं है; शॉर्ट नोटिस पर मुझे जो सबसे अच्छा लगा वह था 14M पेडिग्री फाइल (लंबी लाइनें जो व्हॉट्सएप-अलग हैं, सिर्फ 12000 लाइनों के नीचे)।

लघु संस्करण: मेफ़ाइल कट विधि की तुलना में तेज़ी से प्रकट होता है, लेकिन सब कुछ की तुलना में धीमा है, इसलिए मैं इसे डड कहूँगा। पूंछ | हेड, OTOH, ऐसा लगता है कि यह सबसे तेज़ हो सकता है, हालांकि इस फ़ाइल के आकार में यह अंतर नहीं है, जो कि sed की तुलना में पर्याप्त है।

$ time head -11000 [filename] | tail -1
[output redacted]

real    0m0.117s

$ time cut -f11000 -d$'\n' [filename]
[output redacted]

real    0m1.081s

$ time awk 'NR == 11000 {print; exit}' [filename]
[output redacted]

real    0m0.058s

$ time perl -wnl -e '$.== 11000 && print && exit;' [filename]
[output redacted]

real    0m0.085s

$ time sed "11000q;d" [filename]
[output redacted]

real    0m0.031s

$ time (mapfile -s 11000 -n 1 ary < [filename]; echo ${ary[0]})
[output redacted]

real    0m0.309s

$ time tail -n+11000 [filename] | head -n1
[output redacted]

real    0m0.028s

उम्मीद है की यह मदद करेगा!


4

दूसरों ने जो उल्लेख किया है उसका उपयोग करते हुए, मैं चाहता था कि यह मेरे बैश शेल में एक त्वरित और बांका कार्य हो।

एक फ़ाइल बनाएँ: ~/.functions

इसमें सामग्री जोड़ें:

getline() { line=$1 sed $line'q;d' $2 }

फिर इसे अपने में जोड़ें ~/.bash_profile:

source ~/.functions

अब जब आप एक नई बैश विंडो खोलते हैं, तो आप केवल फंक्शन को कॉल कर सकते हैं:

getline 441 myfile.txt


3

यदि आपको \ n (सामान्य रूप से नई लाइन) द्वारा सीमांकित कई लाइनें मिली हैं। आप 'कट' का भी उपयोग कर सकते हैं:

echo "$data" | cut -f2 -d$'\n'

आपको फ़ाइल से दूसरी पंक्ति मिल जाएगी। -f3आपको 3 लाइन देता है।


1
कई लाइनों को प्रदर्शित करने के लिए भी इस्तेमाल किया जा सकता है: cat FILE | cut -f2,5 -d$'\n'FILE की लाइनों 2 और 5 को प्रदर्शित करेगा। (लेकिन यह आदेश को संरक्षित नहीं करेगा।)
एंड्री मकुक्खा

2

Nth लाइन को एक संख्या के रूप में एक चर के साथ sed का उपयोग करके प्रिंट करने के लिए:

a=4
sed -e $a'q:d' file

यहां '-ई' झंडे को स्क्रिप्ट में जोड़ने के लिए निष्पादित करने के लिए कमांड करने के लिए है।


2
बृहदान्त्र एक वाक्यविन्यास त्रुटि है, और एक अर्धविराम होना चाहिए।
ट्रिपल एफ

2

पहले से ही बहुत अच्छे जवाब। मैं व्यक्तिगत रूप से awk के साथ जाता हूं। सुविधा के लिए, यदि आप बैश का उपयोग करते हैं, तो बस अपने नीचे जोड़ें ~/.bash_profile। और, अगली बार जब आप लॉग इन (या यदि आप इस अद्यतन के बाद अपने .bash_profile स्रोत), आप के माध्यम से अपनी फ़ाइलों को पाइप करने के लिए एक नया निफ्टी "nth" फ़ंक्शन उपलब्ध होगा।

इसे निष्पादित करें या इसे अपने ~ / .bash_profile (यदि bash का उपयोग कर रहे हैं) और फिर से bash (या निष्पादित करें source ~/.bach_profile) में डालें

# print just the nth piped in line nth () { awk -vlnum=${1} 'NR==lnum {print; exit}'; }

फिर, इसका उपयोग करने के लिए, बस इसके माध्यम से पाइप करें। उदाहरण के लिए ,:

$ yes line | cat -n | nth 5 5 line


1

पर एक नज़र लेने के बाद शीर्ष जवाब और बेंचमार्क , मैं एक छोटे से सहायक समारोह को क्रियान्वित किया है:

function nth {
    if (( ${#} < 1 || ${#} > 2 )); then
        echo -e "usage: $0 \e[4mline\e[0m [\e[4mfile\e[0m]"
        return 1
    fi
    if (( ${#} > 1 )); then
        sed "$1q;d" $2
    else
        sed "$1q;d"
    fi
}

मूल रूप से आप इसे दो फैशन में उपयोग कर सकते हैं:

nth 42 myfile.txt
do_stuff | nth 42

0

मैंने ऊपर दिए गए कुछ उत्तरों को एक संक्षिप्त बैश स्क्रिप्ट में डाला है जिसे आप नामक एक फ़ाइल में डाल सकते हैं get.shऔर लिंक कर सकते हैं /usr/local/bin/get(या जो भी अन्य नाम आप पसंद करते हैं)।

#!/bin/bash
if [ "${1}" == "" ]; then
    echo "error: blank line number";
    exit 1
fi
re='^[0-9]+$'
if ! [[ $1 =~ $re ]] ; then
    echo "error: line number arg not a number";
    exit 1
fi
if [ "${2}" == "" ]; then
    echo "error: blank file name";
    exit 1
fi
sed "${1}q;d" $2;
exit 0

सुनिश्चित करें कि यह निष्पादन योग्य है

$ chmod +x get

यह लिंक इस पर उपलब्ध बनाने के लिए PATHसाथ

$ ln -s get.sh /usr/local/bin/get

जिम्मेदारी के साथ उपभाेग कीजिए!

पी

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