বিড়াল লাইন এক্স থেকে লাইন এক বিশাল ফাইলে


132

আমি একটি বিশাল টেক্সট ফাইল আছে (> 2GB) এবং আমি শুধু চাই বলুন catলাইন Xথেকে Y(যেমন 57890010 থেকে 57890000)।

আমি যা বুঝতে থেকে আমি বংশীধ্বনিতুল্য এটা করতে পারেন headমধ্যে tailবা viceversa, অর্থাত্

head -A /path/to/file | tail -B

বা বিকল্পভাবে

tail -C /path/to/file | head -D

যেখানে A, B, Cএবং Dফাইলে লাইনের নম্বর থেকে নির্ণিত করা যেতে পারে, Xএবং Y

তবে এই পদ্ধতির সাথে দুটি সমস্যা রয়েছে:

  1. আপনি গনা আছে A, B, Cএবং D
  2. কমান্ডগুলি পড়ার প্রতি আমার আগ্রহের চেয়ে pipeএকে অপরের আরও অনেকগুলি লাইন থাকতে পারে (যেমন, যদি আমি একটি বিশাল ফাইলের মাঝখানে কয়েকটি লাইন পড়ছি)

শেলটি কীভাবে কাজ করতে পারে তার সাথে কী কাজ করতে পারে এবং আমি যে লাইনে চাই তার আউটপুট দেয়? (কেবল Xএবং সরবরাহের সময় Y)?


1
এফওয়াইআই, 6 টি পদ্ধতির প্রকৃত গতির পরীক্ষার তুলনা আমার উত্তরটিতে যুক্ত হয়েছে।
কেভিন

উত্তর:


119

আমি sedসমাধানটি পরামর্শ দিচ্ছি , তবে সম্পূর্ণতার জন্য,

awk 'NR >= 57890000 && NR <= 57890010' /path/to/file

শেষ লাইনের পরে কাটাতে:

awk 'NR < 57890000 { next } { print } NR == 57890010 { exit }' /path/to/file

গতি পরীক্ষা:

  • 100,000,000-লাইন ফাইল দ্বারা উত্পাদিত seq 100000000 > test.in
  • 50,000,000-50,000,010 লাইন পড়ছে
  • কোন নির্দিষ্ট ক্রমে পরীক্ষা
  • realসময় হিসাবে bashবিল্টিন দ্বারা রিপোর্টtime
 4.373  4.418  4.395    tail -n+50000000 test.in | head -n10
 5.210  5.179  6.181    sed -n '50000000,50000010p;57890010q' test.in
 5.525  5.475  5.488    head -n50000010 test.in | tail -n10
 8.497  8.352  8.438    sed -n '50000000,50000010p' test.in
22.826 23.154 23.195    tail -n50000001 test.in | head -n10
25.694 25.908 27.638    ed -s test.in <<<"50000000,50000010p"
31.348 28.140 30.574    awk 'NR<57890000{next}1;NR==57890010{exit}' test.in
51.359 50.919 51.127    awk 'NR >= 57890000 && NR <= 57890010' test.in

এগুলি কোনওভাবেই সুনির্দিষ্ট মানদণ্ড নয়, তবে পার্থক্যটি পরিষ্কার এবং পুনরাবৃত্তিযোগ্য * এই প্রতিটি আদেশের আপেক্ষিক গতি সম্পর্কে ভাল ধারণা দিতে পারে।

*: প্রথম দুটির মধ্যে sed -n p;qএবং head|tail, যা মূলত একই বলে মনে হয়।


11
কৌতূহলের বাইরে: আপনি কীভাবে পরীক্ষার মধ্যে ডিস্ক ক্যাশে ফেলেছেন?
পাওয়ে রুমিয়ান

2
কী সম্পর্কে tail -n +50000000 test.in | head -n10, যা tail -n-50000000 test.in | head -n10সঠিক ফলাফল দেয় না তার বিপরীতে ?
গিলস

4
ঠিক আছে, আমি গিয়েছিলাম এবং কিছু মানদণ্ড করেছি। লেজ | মাথা সেডের চেয়ে অনেক দ্রুত, পার্থক্যটি আমার প্রত্যাশার চেয়ে অনেক বেশি।
গিলস

3
@ গিলস আপনি ঠিক বলেছেন, আমার খারাপ। tail+|headসেডের চেয়ে 10-15% বেশি দ্রুত, আমি সেই মানদণ্ডটি যুক্ত করেছি।
কেভিন

1
আমি বুঝতে পারি যে প্রশ্নটি লাইনগুলির জন্য জিজ্ঞাসা করে, তবে আপনি যদি -cঅক্ষরগুলি এড়িয়ে যেতে ব্যবহার করেন tail+|headতা তাত্ক্ষণিক। অবশ্যই, আপনি "50000000" বলতে পারবেন না এবং আপনি যে বিভাগটি সন্ধান করছেন তার ম্যানুয়ালি ম্যানুয়ালি খুঁজতে হবে।
ড্যানি কির্চমিয়ার

51

আপনি যদি X থেকে Y সমেত লাইনগুলি চান (1 তে সংখ্যাটি শুরু করে), ব্যবহার করুন

tail -n +$X /path/to/file | head -n $((Y-X+1))

tailপ্রথম এক্স -১ লাইনগুলি পড়বে এবং ফেলে দেবে (এর আশেপাশে কোনও উপায় নেই), তারপরে নীচের লাইনগুলি পড়ুন এবং মুদ্রণ করুন। headঅনুরোধ করা লাইনগুলির সংখ্যা পড়ুন এবং মুদ্রণ করবেন, তারপরে প্রস্থান করুন। যখন headপ্রস্থান করে, tailএকটি পায় SIGPIPE সংকেত মরে, তাই এটি ইনপুট ফাইল থেকে লাইনের একটি বাফারের আকার এর মূল্য (সাধারণত কয়েক কিলোবাইট) চেয়ে আরও পড়তে হবে না।

বিকল্প হিসাবে, gorkypl পরামর্শ হিসাবে, সেড ব্যবহার:

sed -n -e "$X,$Y p" -e "$Y q" /path/to/file

সেড দ্রবণটি উল্লেখযোগ্যভাবে ধীর হলেও (কমপক্ষে জিএনইউ ইউটিলিটিস এবং ব্যাসিবক্স ইউটিলিটির জন্য; যদি আপনি কোনও ওএসে ফাইলের একটি বৃহত অংশ বের করেন যেখানে পাইপিং ধীর এবং সিড দ্রুত হয়)। এখানে লিনাক্সের অধীনে দ্রুত মাপদণ্ড রয়েছে; ডেটা দ্বারা উত্পাদিত হয়েছিল seq 100000000 >/tmp/a, পরিবেশটি লিনাক্স / amd64, /tmptmpfs এবং মেশিনটি অন্যথায় অলস এবং অদলবদল নয়।

real  user  sys    command
 0.47  0.32  0.12  </tmp/a tail -n +50000001 | head -n 10 #GNU
 0.86  0.64  0.21  </tmp/a tail -n +50000001 | head -n 10 #BusyBox
 3.57  3.41  0.14  sed -n -e '50000000,50000010 p' -e '50000010q' /tmp/a #GNU
11.91 11.68  0.14  sed -n -e '50000000,50000010 p' -e '50000010q' /tmp/a #BusyBox
 1.04  0.60  0.46  </tmp/a tail -n +50000001 | head -n 40000001 >/dev/null #GNU
 7.12  6.58  0.55  </tmp/a tail -n +50000001 | head -n 40000001 >/dev/null #BusyBox
 9.95  9.54  0.28  sed -n -e '50000000,90000000 p' -e '90000000q' /tmp/a >/dev/null #GNU
23.76 23.13  0.31  sed -n -e '50000000,90000000 p' -e '90000000q' /tmp/a >/dev/null #BusyBox

আপনি যে বাইট রেঞ্জের সাথে কাজ করতে চান তা যদি আপনি জানেন তবে সরাসরি শুরুর অবস্থানে এড়িয়ে আপনি এটিকে আরও দ্রুত বের করতে পারবেন। তবে লাইনের জন্য আপনাকে প্রথম থেকেই পড়তে হবে এবং নিউলাইনগুলি গণনা করতে হবে। X এর সমাপ্ত থেকে y এক্সক্লুসিভ থেকে 0 থেকে বি এর ব্লকের আকারের সাথে ব্লকগুলি বের করতে:

dd bs=$b seek=$x count=$((y-x)) </path/to/file

1
আপনি কি নিশ্চিত যে অভ্যন্তরে কোনও ক্যাশিং নেই? লেজ | মাথা এবং সেডের মধ্যে পার্থক্যগুলি আমার কাছে খুব বড় বলে মনে হচ্ছে।
পাওয়ে রুমিয়ান

@ gorkypl আমি বেশ কয়েকটি ব্যবস্থা গ্রহণ করেছি এবং সময়গুলি তুলনীয় ছিল। আমি যেমন লিখেছি, র্যামে এটিই ঘটছে (সবকিছুই ক্যাশে রয়েছে)।
গিলস

1
tail will read and discard the first X-1 lineশেষ থেকে রেখার সংখ্যাটি দেওয়া হলে @ গিলস এড়ানো হবে বলে মনে হয়, এরকম ক্ষেত্রে নির্বাহের সময় অনুসারে লেজটি শেষ থেকে পিছনের দিকে পড়ে বলে মনে হয়। অনুগ্রহ করে পড়ুন: http://unix.stackexchange.com/a/216614/79743

1
@ বাইনারিজেব্রা হ্যাঁ, ইনপুটটি যদি নিয়মিত ফাইল হয় তবে কিছু বাস্তবায়ন tail(জিএনইউ লেজ সহ) শেষ থেকে পড়ার জন্য হিউরিস্টিকস রয়েছে। যা tail | headঅন্যান্য পদ্ধতির তুলনায় সমাধানের উন্নতি করে ।
গিলস

22

head | tailপদ্ধতির সবচেয়ে ভাল এবং সবচেয়ে "কথ্য" এই কাজ করতে উপায়ে এগুলির মধ্যে একটি:

X=57890000
Y=57890010
< infile.txt head -n "$Y" | tail -n +"$X"

মন্তব্যগুলিতে গিলস দ্বারা নির্দেশিত হিসাবে, একটি দ্রুত উপায়

< infile.txt tail -n +"$X" | head -n "$((Y - X))"

এটি দ্রুত হওয়ার কারণটি হল প্রথম এক্স - 1 টি লাইনের সাথে head | tailপদ্ধতির তুলনায় পাইপটি দিয়ে যাওয়ার দরকার নেই ।

বর্ণিত হিসাবে আপনার প্রশ্নটি কিছুটা বিভ্রান্তিকর এবং সম্ভবত এই পদ্ধতির দিকে আপনার কিছু ভিত্তিহীন বিভ্রান্তি ব্যাখ্যা করে।

  • আপনি বলতে আপনি নিরূপণ করা আছে A, B, C, Dকিন্তু আপনি দেখতে পারেন, ফাইলের লাইন গণনা প্রয়োজন নেই এবং সবচেয়ে 1 হিসাব প্রয়োজনীয়, যা শেল কোন পথে তোমার জন্য কি করতে পারেন।

  • আপনি উদ্বেগ করছেন যে পাইপিং প্রয়োজনীয়তার চেয়ে আরও বেশি লাইন পড়বে। আসলে এটি সত্য নয়: tail | headআপনি আই / ও ফাইলের ক্ষেত্রে যতটা দক্ষতা অর্জন করতে পারেন তেমন দক্ষ। প্রথমে প্রয়োজনীয় ন্যূনতম পরিমাণটি বিবেচনা করুন: কোনও ফাইলের এক্স 'র লাইনটি সন্ধান করার জন্য, একমাত্র সাধারণ উপায় হ'ল প্রতি বাইট পড়ুন এবং যখন আপনি এক্স নিউলাইন প্রতীকগুলি গণনা করছেন তখন ফাইলটি divineশ্বরিকভাবে করার কোন উপায় নেই stop এক্স 'ম লাইনের অফসেট । একবার আপনি * এক্স * তম লাইনে পৌঁছানোর পরে, আপনার সমস্ত লাইনগুলি মুদ্রণের জন্য পড়তে হবে, ওয়াই 'র লাইনে থামবেন। এইভাবে কোনও পন্থা ওয়াই লাইনের চেয়ে কম পাঠ করে দূরে যেতে পারে না । এখন, Y এরhead -n $Y চেয়ে বেশি কিছু পড়ছে নালাইনগুলি (নিকটতম বাফার ইউনিটকে গোলাকার, তবে বাফারগুলি সঠিকভাবে কর্মক্ষমতা উন্নত করতে ব্যবহৃত হয়, সুতরাং সেই ওভারহেড সম্পর্কে চিন্তা করার দরকার নেই)। উপরন্তু, tailছাড়া আর পড়বো না? head, তাই এইভাবে আমরা দেখা গেছে head | tailসম্ভব লাইনের fewest নম্বর (আবার, প্লাস কিছু তুচ্ছ বাফার উপলব্ধ যে আমরা উপেক্ষা করা হয়) পড়া হয়। পাইপ ব্যবহার না করে এমন একক সরঞ্জাম পদ্ধতির একমাত্র দক্ষতার সুবিধা হ'ল কম প্রক্রিয়া (এবং এইভাবে কম ওভারহেড)।


1
পুনর্নির্দেশটি আগে কখনও লাইনে যেতে দেখেনি। শীতল, এটি পাইপ প্রবাহকে আরও পরিষ্কার করে তোলে।
ক্যালক

14

সর্বাধিক গোঁড়া উপায় (তবে উপরে গিলস দ্বারা উল্লিখিত দ্রুত নয় ) ব্যবহার করা হবে sed

আপনার ক্ষেত্রে:

X=57890000
Y=57890010
sed -n -e "$X,$Y p" -e "$Y q" filename

-nবিকল্প যে বোঝা শুধুমাত্র প্রাসঙ্গিক লাইন stdout- এ ছাপা হয়।

পি লাইন সংখ্যা শেষ হচ্ছে শেষে প্রদত্ত ব্যাপ্তির মধ্যে লাইন প্রিন্ট করতে মানে। কুই স্ক্রিপ্ট দ্বিতীয় অংশে ফাইলের বাকি কুঁদন করে কিছু সময় বাঁচায়।


1
আমি প্রত্যাশা করেছি sedএবং tail | headসমান হয়ে যাব , তবে এটি সক্রিয় হয়েছে যা tail | headউল্লেখযোগ্যভাবে দ্রুত ( আমার উত্তর দেখুন )।
গিলস

1
আমি জানি না, আমি যা পড়েছি tail/ headসেগুলিকে আরও "গোঁড়া" হিসাবে বিবেচনা করা হয়, যেহেতু কোনও ফাইলের উভয় প্রান্তকে ছাঁটাই করা তারা ঠিক কীভাবে তৈরি করেছে is এই উপকরণগুলিতে sedকেবলমাত্র বিকল্পগুলির প্রয়োজন হলে ছবিটি প্রবেশ করানো মনে হয় - এবং আরও জটিল কিছু ঘটতে শুরু করার সাথে সাথে দ্রুত চিত্রের বাইরে চলে যেতে হয়, কারণ জটিল কাজের জন্য এর বাক্য গঠনটি এডাব্লুকে-র চেয়ে আরও খারাপ, যা তার পরে গ্রহণ করে ।
আন্ডারস্কোর_২

7

আমরা যদি প্রথম লাইন থেকে শেষ লাইন পর্যন্ত নির্বাচন করতে হয় তার সীমাটি জানতে পারি lStart: lEndআমরা গণনা করতে পারি:

lCount="$((lEnd-lStart+1))"

যদি আমরা লাইনগুলির মোট পরিমাণ জানি: lAllআমরা ফাইলের শেষের দূরত্বও গণনা করতে পারি:

toEnd="$((lAll-lStart+1))"

তাহলে আমরা উভয়কেই জানব:

"how far from the start"            ($lStart) and
"how far from the end of the file"  ($toEnd).

এর মধ্যে যে কোনওটির মধ্যে সবচেয়ে ছোটটি চয়ন করা: এটি tailnumberহিসাবে:

tailnumber="$toEnd"; (( toEnd > lStart )) && tailnumber="+$linestart"

আমাদের ধারাবাহিকভাবে দ্রুততম নির্বাহকারী কমান্ডটি ব্যবহার করতে অনুমতি দেয়:

tail -n"${tailnumber}" ${thefile} | head -n${lCount}

নির্বাচিত হওয়ার পরে দয়া করে অতিরিক্ত প্লাস ("+") চিহ্নটি নোট করুন $linestart

একমাত্র সতর্কতাই হ'ল আমাদের মোট লাইনের গণনা প্রয়োজন এবং এটি পেতে কিছু অতিরিক্ত সময় নিতে পারে।
যেমনটি যথারীতি:

linesall="$(wc -l < "$thefile" )"

কিছু সময় পরিমাপ করা হয়:

lStart |500| lEnd |500| lCount |11|
real   user   sys    frac
0.002  0.000  0.000  0.00  | command == tail -n"+500" test.in | head -n1
0.002  0.000  0.000  0.00  | command == tail -n+500 test.in | head -n1
3.230  2.520  0.700  99.68 | command == tail -n99999501 test.in | head -n1
0.001  0.000  0.000  0.00  | command == head -n500 test.in | tail -n1
0.001  0.000  0.000  0.00  | command == sed -n -e "500,500p;500q" test.in
0.002  0.000  0.000  0.00  | command == awk 'NR<'500'{next}1;NR=='500'{exit}' test.in


lStart |50000000| lEnd |50000010| lCount |11|
real   user   sys    frac
0.977  0.644  0.328  99.50 | command == tail -n"+50000000" test.in | head -n11
1.069  0.756  0.308  99.58 | command == tail -n+50000000 test.in | head -n11
1.823  1.512  0.308  99.85 | command == tail -n50000001 test.in | head -n11
1.950  2.396  1.284  188.77| command == head -n50000010 test.in | tail -n11
5.477  5.116  0.348  99.76 | command == sed -n -e "50000000,50000010p;50000010q" test.in
10.124  9.669  0.448  99.92| command == awk 'NR<'50000000'{next}1;NR=='50000010'{exit}' test.in


lStart |99999000| lEnd |99999010| lCount |11|
real   user   sys    frac
0.001  0.000  0.000  0.00  | command == tail -n"1001" test.in | head -n11
1.960  1.292  0.660  99.61 | command == tail -n+99999000 test.in | head -n11
0.001  0.000  0.000  0.00  | command == tail -n1001 test.in | head -n11
4.043  4.704  2.704  183.25| command == head -n99999010 test.in | tail -n11
10.346  9.641  0.692  99.88| command == sed -n -e "99999000,99999010p;99999010q" test.in
21.653  20.873  0.744  99.83 | command == awk 'NR<'99999000'{next}1;NR=='99999010'{exit}' test.in

নোট করুন যে নির্বাচিত লাইনগুলি শুরুতে বা শেষের কাছাকাছি থাকলে সময়গুলি তাত্পর্যপূর্ণভাবে পরিবর্তন হয়। একটি কমান্ড যা ফাইলের একপাশে সুন্দরভাবে কাজ করতে দেখা যায়, ফাইলের অন্য দিকে খুব ধীর হতে পারে।


মন্তব্যগুলি বর্ধিত আলোচনার জন্য নয়; এই কথোপকথন চ্যাটে সরানো হয়েছে ।
টেরডন

@ বাইনারি জেব্রা - আরও ভাল উপায়
মাইকজার্ভ

0

আমি প্রায়শই এটি যথেষ্ট করি এবং তাই এই স্ক্রিপ্টটি লিখেছিলাম। আমার লাইন নম্বরগুলি সন্ধান করার দরকার নেই, স্ক্রিপ্টটি এটি সবই করে।

#!/bin/bash

# $1: start time
# $2: end time
# $3: log file to read
# $4: output file

# i.e. log_slice.sh 18:33 19:40 /var/log/my.log /var/log/myslice.log

if [[ $# != 4 ]] ; then 
echo 'usage: log_slice.sh <start time> <end time> <log file> <output file>'
echo
exit;
fi

if [ ! -f $3 ] ; then
echo "'$3' doesn't seem to exit."
echo 'exiting.'
exit;
fi

sline=$(grep -n " ${1}" $3|head -1|cut -d: -f1)  #what line number is first occurrance of start time
eline=$(grep -n " ${2}" $3|head -1|cut -d: -f1)  #what line number is first occurrance of end time

linediff="$((eline-sline))"

tail -n+${sline} $3|head -n$linediff > $4

2
আপনি এমন প্রশ্নের উত্তর দিচ্ছেন যা জিজ্ঞাসা করা হয়নি। আপনার উত্তর 10% tail|head, যা প্রশ্ন এবং অন্যান্য উত্তরগুলিতে ব্যাপক আলোচনা করা হয়েছে, এবং 90% নির্দিষ্ট রেখা / প্যাটার্নগুলি উপস্থিত রয়েছে এমন রেখা সংখ্যা নির্ধারণ করে, যা প্রশ্নের অংশ ছিল না । পিএস আপনার সর্বদা আপনার শেল প্যারামিটার এবং ভেরিয়েবল উদ্ধৃত করা উচিত; যেমন, "$ 3" এবং "$ 4"।
জি ম্যান
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.