किसी फ़ाइल की पहली और अंतिम कुछ पंक्तियों को प्रदर्शित करने के लिए कमांड


23

मेरे पास कई पंक्तियों वाली फाइल है, और प्रत्येक पंक्ति में शुरुआत में टाइमस्टैम्प है, जैसे

[Thread-3] (21/09/12 06:17:38:672) logged message from code.....

इसलिए, मैं अक्सर इस लॉग फ़ाइल से 2 चीजों की जांच करता हूं।

  1. पहली कुछ पंक्तियाँ, जिनमें वैश्विक स्थितियाँ हैं और आरंभ समय भी दिया गया है।
  2. अंतिम कुछ पंक्तियाँ, जिसमें कुछ अन्य जानकारी के साथ निकास की स्थिति है।

क्या कोई त्वरित आसान एकल कमांड है जो मुझे फ़ाइल के पहले और अंतिम कुछ लाइनों को प्रदर्शित कर सकती है?


2
वैश्विक स्थिति क्या है, और head and tailआपके लिए काम नहीं करती है?
डेज़ी

यह मेरी लॉग फ़ाइल का हिस्सा है। मैं विस्तृत होने की कोशिश कर रहा था। आप इसे अनदेखा कर सकते हैं।
एमटीके

आपका समाधान मुझे ठीक लग रहा है। यदि आप अधिक सुविधा चाहते हैं, तो इसे शेल फ़ंक्शन (यहां तक ​​कि एक उपनाम भी कर सकते हैं) में बनाएं।
वॉनब्रांड

@vonbrand समस्या यह है कि मैं नहीं जानताN
बर्नहार्ड

@ बर्नहार्ड, मैं कोई sed(1)विशेषज्ञ नहीं हूं , लेकिन बाद में इसके साथ उपयोग करने के लिए सामान को दूर करने के तरीके हैं। शायद यह वहाँ में देखने के लिए भुगतान करता है। OTOH, मैं शायद एक पर्ल (या जो कुछ भी) स्क्रिप्ट को यह करने के लिए इस्तेमाल करूँगा अगर मैं अक्सर उपयोग किया जाता हूं, तो मैं इससे परिचित हूं।
वॉनब्रांड

जवाबों:


12

आप इसे एक आदेश के साथ उपयोग sedया awkबना सकते हैं । हालाँकि आप गति में ढीले हैं, कारण sedऔर awkवैसे भी पूरी फ़ाइल के माध्यम से चलाने की आवश्यकता होगी। एक गति बिंदु से यह फ़ंक्शन या हर बार tail+ के संयोजन को बनाने के लिए बेहतर है head। यदि इनपुट एक पाइप है, तो यह काम न करने का नकारात्मक पक्ष है, हालांकि, यदि आप अपने शेल का समर्थन करते हैं, तो नीचे दिए गए विकल्प का उपयोग कर सकते हैं।

first_last () {
    head -n 10 -- "$1"
    tail -n 10 -- "$1"
}

और अभी इसे लॉन्च करें

first_last "/path/to/file_to_process"

प्रक्रिया प्रतिस्थापन के साथ आगे बढ़ने के लिए (बाश, zsh, ksh केवल गोले की तरह):

first_last <( command )

ps। grepयदि आपकी "वैश्विक स्थितियाँ" मौजूद हैं, तो आप यह जाँचने के लिए भी जोड़ सकते हैं।


-n 10डिफ़ॉल्ट है, नहीं?
l0b0

@ l0b0 हां, यह डिफ़ॉल्ट है। -n 10यहाँ आवश्यक नहीं है।
भीड़

20

@rush बड़ी फ़ाइलों के लिए हेड + टेल के अधिक कुशल होने के बारे में सही है, लेकिन छोटी फ़ाइलों (<20 लाइनों) के लिए, कुछ लाइनें दो बार आउटपुट हो सकती हैं।

{ head; tail;} < /path/to/file

समान रूप से कुशल होगा, लेकिन ऊपर समस्या नहीं होगी।


भीड़ समाधान के विपरीत, यह एक POSIX शेल में काम नहीं करता है।
मार्को

2
@ मर्को हुह? यहां केवल POSIX निर्माण का उपयोग किया जाता है। आप गलत होते हुए क्या देखते हैं?
गिलेस एसओ- बुराई को रोकना '

2
@ गिल्स मैं जगह से चूक गया: {head; tail;} < filezsh में काम करता है लेकिन श में विफल रहता है। { head; tail;} < fileहमेशा काम करता है। शोर के लिए क्षमा करें।
मार्को

@ मार्को, अगर इसके साथ समस्याएं थीं, तो यह headशेल के साथ होगी । POSIX को headनियमित फ़ाइलों के लिए उन 10 लाइनों को अतीत में फ़ाइल में कर्सर छोड़ने की आवश्यकता है। गैर- POSIX headकार्यान्वयन के लिए एक समस्या उत्पन्न हो सकती है (GNU हेड के बहुत पुराने संस्करण उस उदाहरण में गैर-अनुरूप हुआ करते थे, लेकिन हम दशकों से बात कर रहे हैं) या यदि फ़ाइल तलाशने योग्य नहीं है (जैसे नाम पाइप या सॉकेट, लेकिन फिर अन्य समाधान एक ही समस्या होगी)।
स्टीफन चेज़लस

1
@ एफसीटीडब्ल्यू,sudo sh -c '{ head; tail;} < /path/to/file'
स्टीफन चेज़लस

9

{ head; tail; }समाधान पाइप (या सॉकेट या किसी अन्य गैर seekable फ़ाइलें) क्योंकि पर काम नहीं होता headबहुत ज्यादा डेटा का उपभोग कर सकता है के रूप में यह ब्लॉक से पढ़ता है और संभावित रूप से परे फाइल के अंदर कर्सर छोड़ने एक पाइप पर वापस मांग नहीं कर सकते क्या tailमतलब है चयन करना।

तो, आप एक उपकरण का उपयोग कर सकते हैं जो एक बार में एक अक्षर को खोल की तरह पढ़ता है read(यहां एक फ़ंक्शन का उपयोग करके जो हेड लाइन और पूंछ लाइनों की संख्या को तर्क के रूप में लेता है)।

head_tail() {
  n=0
  while [ "$n" -lt "$1" ]; do
    IFS= read -r line || { printf %s "$line"; break; }
    printf '%s\n' "$line"
    n=$(($n + 1))
  done
  tail -n "${2-$1}"
}
seq 100 | head_tail 5 10
seq 20 | head_tail 5

या tailउदाहरण के लिए awk में लागू करें :

head_tail() {
  awk -v h="$1" -v t="${2-$1}" '
    {l[NR%t]=$0}
    NR<=h
    END{
      n=NR-t+1
      if(n <= h) n = h+1
      for (;n<=NR;n++) print l[n%t]
    }'
}

के साथ sed:

head_tail() {
  sed -e "1,${1}b" -e :1 -e "$(($1+${2-$1})),\$!{N;b1" -e '}' -e 'N;D'
}

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


4

bashप्रक्रिया प्रतिस्थापन का उपयोग करना , आप निम्नलिखित कर सकते हैं:

make_some_output | tee >(tail -n 2) >(head -n 2; cat >/dev/null) >/dev/null

ध्यान दें कि लाइनों को क्रम में होने की गारंटी नहीं है, हालांकि फ़ाइलों के बारे में 8kB से अधिक समय के लिए, वे बहुत संभावना होगी। यह 8kB कटऑफ रीड बफर का विशिष्ट आकार है, और इस कारण से संबंधित है जो | {head; tail;}छोटी फ़ाइलों के लिए काम नहीं करता है।

cat >/dev/nullरखने के लिए आवश्यक है headपाइप लाइन जिंदा। अन्यथा teeजल्दी छोड़ देंगे, और जब आप आउटपुट प्राप्त करेंगे tail, तो यह अंत के बजाय इनपुट के बीच में कहीं से होगा।

अंत में, >/dev/nullइसके बजाय, tailदूसरे के पास जाने की बात |क्यों? निम्नलिखित मामले में:

make_some_output | tee >(head -n 2; cat >/dev/null) | tail -n 2  # doesn't work

headtailकंसोल के बजाय पाइप में फीड किया जाता है, जो कि हम बिल्कुल नहीं चाहते हैं।


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

क्या आदेश को बरकरार रखने की संभावना है? यह शायद एक बड़ी फ़ाइल के लिए होगा, क्योंकि tailइसमें अधिक समय तक काम करना पड़ता है, लेकिन मुझे उम्मीद है (और देखते हैं) यह छोटे इनपुट के बारे में आधे समय में विफल हो रहा है।
गिल्स एसओ- बुराई को रोकना '

आप tee >(head) >(tail)उन्हीं कारणों से SIGPIPE प्राप्त करेंगे ( >(...)जो वैसे भी एक ksh फीचर है जो अब zsh और bash दोनों द्वारा समर्थित है) पाइप का भी उपयोग करता है। आप कर सकते हैं ... | (trap '' PIPE; tee >(head) >(tail) > /dev/null)लेकिन फिर भी आपको कुछ टूटे हुए पाइप त्रुटि संदेश दिखाई देंगे tee
स्टीफन चेज़लस

मेरे सिस्टम पर (4.2.37, कोरुटिल्स 8.13 को बैश), tailSIGPIPE द्वारा मारा जा रहा है, नहीं tee, और tailपाइप पर नहीं लिख रहा है। तो यह kill()सही से होना चाहिए ? और यह केवल तब होता है जब मैं |वाक्य रचना का उपयोग कर रहा होता हूं । straceकहते हैं कि teeफोन नहीं कर रहा है kill()... तो शायद bash?
जंतर

1
@ जेंडर, 8k से अधिक खिलाने की कोशिश करें जैसेseq 100000 | tee >(head -n1) >(tail -n1) > /dev/null
स्टीफन चेज़लस

3

उपयोग करना ed(जो रैम में पूरी फ़ाइल पढ़ेगा):

# cf. http://wiki.bash-hackers.org/howto/edit-ed
printf '%s\n' 'H' '1,10p' '$-10,$p' 'q' | ed -s file

छोटा:ed -s file <<< $'11,$-10d\n,p\nq\n'
don_crissti

2

एक समारोह में स्टीफन का पहला समाधान ताकि आप तर्कों का उपयोग कर सकें (किसी बॉर्न-जैसे या पॉसिक्स शेल में काम करता है):

head_tail() {
    head "$@";
    tail "$@";
}

अब आप यह कर सकते हैं:

head_tail -n 5 < /path/to/file

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


2

जीएनयू के विकल्प के साथ -u( --unbuffered) विकल्प के रूप में sedउपयोग sed -u 2qकिया जा सकता है head -n2:

$ seq 100|(sed -u 2q;tail -n2)
1
2
99
100

(head -n2;tail -n2)विफल हो जाता है जब अंतिम लाइनें उस इनपुट के ब्लॉक का हिस्सा होती हैं जिसका उपभोग किया जाता है head:

$ seq 1000|(head -n2;tail -n2)
1
2
999
1000
$ seq 100|(head -n2;tail -n2)
1
2

यह शीर्ष उत्तर होना चाहिए! एक जादू की तरह काम करता है!
बेन उस्मान

1

मैं आज कुछ इस तरह से भाग गया जहाँ मुझे एक धारा के सामने से केवल अंतिम पंक्ति और कुछ पंक्तियों की आवश्यकता थी और निम्नलिखित के साथ आया।

sed -n -e '1{h}' -e '2,3{H}' -e '${H;x;p}'

मैं इसे इस रूप में पढ़ता हूं: पहली पंक्ति की सामग्री के साथ होल्ड स्पेस को इनिशियलाइज़ करें, होल्ड स्पेस में 2-3 को जोड़ें, ईओएफ में अंतिम लाइन को होल्ड स्पेस पर रखें, होल्ड-एंड-पैटर्न स्पेस को स्वैप करें, और पैटर्न प्रिंट करें अंतरिक्ष।

शायद sedमेरे पास जितने भी लोग हैं, उनमें से किसी के पास इस प्रश्न में बताई गई धारा की अंतिम कुछ पंक्तियों को प्रिंट करने के लिए सामान्यीकरण कैसे किया जा सकता है, लेकिन मुझे इसकी आवश्यकता नहीं थी और $पता के आधार पर गणित करने का एक आसान तरीका नहीं मिल सकता है में sedया शायद पकड़ अंतरिक्ष इतना प्रबंध है कि केवल पिछले कुछ लाइनों जब यह कर रहे हैं द्वारा EOFपहुँच जाता है।


1

यदि आपने इसे स्थापित किया है, तो आप पर्ल की कोशिश कर सकते हैं:

perl -e '@_ = <>; @_=@_[0, -3..-1]; print @_'

यह अधिकांश फ़ाइलों के लिए काम करेगा, लेकिन इसे संसाधित करने से पहले पूरी फ़ाइल को मेमोरी में पढ़ता है। यदि आप पर्ल स्लाइस से परिचित नहीं हैं, तो वर्ग कोष्ठक में "0" का अर्थ है "पहली पंक्ति लें", और "-3 ...- 1" का अर्थ है "अंतिम तीन पंक्तियाँ लें"। आप अपनी आवश्यकताओं के लिए उन दोनों को दर्जी कर सकते हैं। यदि आपको वास्तव में बड़ी फ़ाइलों को संसाधित करने की आवश्यकता है (जो कि 'बड़ी' आपके रैम और शायद स्वैप आकार पर निर्भर हो सकती है), आप इसके लिए जाना चाहते हैं:

perl -e 'while($_=<>){@_=(@_,$_)[0,-3..-1]}; print @_'

यह कुछ धीमा हो सकता है, क्योंकि यह हर पुनरावृत्ति को एक टुकड़ा बनाता है, लेकिन यह फ़ाइल आकार पर स्वतंत्र है।

दोनों कमांड को पाइप में और नियमित फाइलों के साथ दोनों काम करना चाहिए।

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