ফাইল থেকে নবম লাইন পাওয়ার জন্য বাশ সরঞ্জাম


603

এটি করার কোনও "প্রচলিত" উপায় আছে? আমি head -n | tail -1কৌতুকটি ব্যবহার করে যাচ্ছি, তবে আমি ভাবছিলাম যে কোনও বাশ সরঞ্জাম রয়েছে যা একটি ফাইল থেকে সুনির্দিষ্টভাবে একটি লাইন (বা লাইনগুলির একটি রেঞ্জ) বের করে।

"ক্যানোনিকাল" বলতে বোঝায় এমন একটি প্রোগ্রাম যার মূল ফাংশন এটি করছে।


10
"ইউনিক্সের উপায়" হ'ল চীন সরঞ্জামগুলি যা তাদের নিজ নিজ কাজটি ভালভাবে করে। সুতরাং আমি মনে করি আপনি ইতিমধ্যে একটি খুব উপযুক্ত পদ্ধতি খুঁজে পেয়েছেন। অন্যান্য পদ্ধতির অন্তর্ভুক্ত রয়েছে awkএবং sedএবং আমি নিশ্চিত যে কেউ পার্ল ওয়ান-লাইনার পাশাপাশি আসতে পারে;)
0xC0000022L

3
ডাবল কমান্ড পরামর্শ দেয় যে head | tailসমাধানটি সর্বোত্তম। আরও প্রায় সর্বোত্তম সমাধানগুলির পরামর্শ দেওয়া হয়েছে।
জোনাথন লেফলার

আপনি কি এমন কোনও বেঞ্চমার্ক চালিয়েছেন যার সমাধানের ক্ষেত্রে গড় ক্ষেত্রে সবচেয়ে দ্রুততম?
মার্সিন

5
এ benchmarks (কাজ করার জন্য) বিড়াল লাইন এক্স বিপুল ফাইলে ওয়াই লাইন উপর ইউনিক্স ও লিনাক্স । (সিসি @ মার্সিন, আপনি যদি আরও দু'বছর পরেও ভাবছেন)
কেভিন

6
head | tailসমাধান, কাজ না করে তাহলে আপনি একটি লাইন যে ইনপুট বিদ্যমান নেই: QUERY গত লাইন প্রিন্ট করবে।
জার্নো

উত্তর:


800

headএবং পাইপ সহ tailএকটি বিশাল ফাইলের জন্য ধীর হবে। আমি sedএই মত পরামর্শ দিতে হবে :

sed 'NUMq;d' file

NUMআপনি যে লাইনের মুদ্রণ করতে চান তার সংখ্যাটি কোথায় ; সুতরাং, উদাহরণস্বরূপ, sed '10q;d' fileএর দশম লাইনটি মুদ্রণ করতে file

ব্যাখ্যা:

NUMqঅবিলম্বে প্রস্থান করা হবে লাইন সংখ্যা যখন NUM

dলাইনটি মুদ্রণের পরিবর্তে মুছে ফেলবে; এটি শেষ লাইনে বাধা দেওয়া হয়েছে কারণ qপ্রস্থানটি ছেড়ে যাওয়ার সময় বাকী স্ক্রিপ্টটি এড়িয়ে যাওয়ার কারণ রয়েছে।

আপনার যদি NUMভেরিয়েবল থাকে তবে আপনি এককটির পরিবর্তে ডাবল কোট ব্যবহার করতে চাইবেন:

sed "${NUM}q;d" file

44
যারা ভাবছেন তাদের জন্য, এই সমাধানটি নীচের প্রস্তাবিত সমাধান sed -n 'NUMp'এবং sed 'NUM!d'সমাধানগুলির চেয়ে প্রায় 6 থেকে 9গুণ দ্রুত বলে মনে হচ্ছে ।
স্কিপি লে গ্র্যান্ড গৌরু

75
আমার মনে tail -n+NUM file | head -n1হয় সম্ভবত তত দ্রুত বা দ্রুত হতে পারে। কমপক্ষে, আমার সিস্টেমে যখন এটি অর্ধ মিলিয়ন লাইনের সাথে NUM তে 250000 থাকার চেষ্টা করেছিল তখন এটি (উল্লেখযোগ্যভাবে) দ্রুত ছিল। ওয়াইএমএমভি, তবে কেন হবে তা আমি সত্যি দেখছি না।
ধনী

2
@rici (পূর্ববর্তী মন্তব্যের পুনর্বিবেচনা) লিনাক্সে (উবুন্টু 12.04, ফেডোরা 20), ব্যবহার catকরা সত্যই দ্রুত (প্রায় দ্বিগুণ দ্রুত) খুব দ্রুত, তবে কেবল যদি ফাইলটি ক্যাশে না করা হয় তবেইএকবার ফাইল ক্যাশে হয়ে গেলে ফাইলের যুক্তির সরাসরি ব্যবহার দ্রুত হয় (প্রায় 1/3 দ্রুত), যখন catপারফরম্যান্স একই থাকে। কৌতূহলজনকভাবে, ওএস এক্স ১০.৯.৩ এ এর ​​কোনওটিই কোনও পার্থক্য বলে মনে হচ্ছে না: cat/ না cat, ফাইল ক্যাশে হয়েছে কি না। @ অনুভা: আমার আনন্দ
mklement0

2
@ স্কিপাইলগ্র্যান্ড গৌড়ো: এই অপ্টিমাইজেশনের নির্দিষ্ট প্রকৃতি দেওয়া , এমনকি আপনার সংখ্যাগুলির পরিসর সাধারণ বিবৃতি হিসাবে অর্থহীন । একমাত্র সাধারণ গ্রহণযোগ্যতা হ'ল: (ক) এই অপ্টিমাইজেশনটি সমস্ত ইনপুটটিতে নিরাপদে প্রয়োগ করা যেতে পারে, (খ) সামগ্রিক রেখার সংখ্যার তুলনায় অনুসন্ধান করা লাইনের সূচকের উপর নির্ভর করে প্রভাবগুলি নাটকীয় থেকে শুরু করে।
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

লাইন 10 লাইন 33 পর্যন্ত

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

1 ম এবং 3 র্থ লাইন

এবং তাই ...

সেড দিয়ে লাইন যুক্ত করার জন্য, আপনি এটি পরীক্ষা করতে পারেন:

সেড: একটি নির্দিষ্ট অবস্থানে একটি লাইন .োকান


6
@ রাফায়েলবারবোসা <এই ক্ষেত্রে প্রয়োজনীয় নয়। সহজভাবে, এটি পুনর্নির্দেশগুলি ব্যবহার করা আমার পছন্দ, কারণ আমি প্রায়শই sed -n '100p' < <(some_command)- তাই, সার্বজনীন সিনট্যাক্স :) এর মতো পুনঃনির্দেশগুলি ব্যবহার করি । এটি কম কার্যকরী নয়, যখন নিজেই forking কারণ ফেরৎ শেল সঙ্গে সম্পন্ন করা হয়, তাই ... এটি শুধুমাত্র একটি পক্ষপাত আছে ... (এবং হ্যাঁ, এটা একটি অক্ষর আর) :)
jm666

1
@ জেএম 66 2 আসলে এটি 2 অক্ষর দীর্ঘতর হওয়ায় আপনি সাধারণত << 'পাশাপাশি একটি অতিরিক্ত স্থান' রাখবেন <আপনি যদি
<<

2
@ rasen58 স্থানটিও একটি চরিত্র? :) / ঠিক আছে, মজা করছি - আপনি ঠিক / :)
jm666

1
@ ডিহাইম অবশ্যই, কারও যদি অপ্টিমাইজেশন করার প্রয়োজন হয়। তবে আইএমএইচও "সাধারণ" সমস্যার জন্য এটি ঠিক আছে এবং পার্থক্যটি অবিস্মরণীয়। এছাড়াও, head/ দৃশ্যের tailসমাধান করে না sed -n '1p;3p'- ওরফে আরও অ-সংলগ্ন সারিগুলি মুদ্রণ করুন ...
jm666

1
অবশ্যই @duhaime - নোটটি সঠিক এবং প্রয়োজনীয়। :)
jm666

93

আমার একটি অনন্য পরিস্থিতি রয়েছে যেখানে আমি এই পৃষ্ঠায় প্রস্তাবিত সমাধানগুলিকে বেনমার্ক করতে পারি, এবং তাই আমি প্রতিটিটির জন্য অন্তর্ভুক্ত রান সময় সহ প্রস্তাবিত সমাধানগুলির একীকরণ হিসাবে এই উত্তরটি লিখছি।

সেট আপ

আমার কাছে প্রতি সারি একটি কী-মান জোড় সহ একটি 3.261 গিগাবাইট ASCII পাঠ্য ডেটা ফাইল রয়েছে। এই ফাইলটিতে মোট ৩,৩৩৯, ৫৫০,৩২০ টি সারি রয়েছে এবং আমার যাওয়া-আসা ভিম সহ আমি চেষ্টা করেছি এমন কোনও সম্পাদকের খোলার বিষয়টি অস্বীকার করে। আমি আবিষ্কার করেছি কেবলমাত্র values ​​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

50 মিলিয়ন সারিটির বেসলাইনটি 00: 01: 15.321, যদি আমি 500 মিলিয়ন সারির জন্য সোজা চলে যাই তবে সম্ভবত এটি 12.5 মিনিট হত।

কাটা

আমি এটি সম্পর্কে সন্দেহজনক, তবে এটি শট করার জন্য মূল্যবান:

$ 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 এ চলেছিল, যা বেসলাইন থেকে seconds 2 সেকেন্ড দ্রুত। আমি যদি এটি পুরো 500,000,000 এ চালাই তবে এটি সম্ভবত 12 মিনিট সময় নিতে পারে।

sed

বোর্ডের শীর্ষ উত্তর, এখানে আমার ফলাফল:

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

real    1m12.705s

এই কোডটি 00: 01: 12.705 এ চলেছিল, যা বেসলাইন থেকে 3 সেকেন্ড দ্রুত এবং পার্লের চেয়ে ~ 0.4 সেকেন্ড দ্রুত। আমি যদি এটি পুরো 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 / নাল মধ্যে সবেমাত্র কতক্ষণ লাগবে। (এটি যদি কেবল একটি হার্ড ডিস্কের মানদণ্ড
হত

আমি আপনার 3+ গিগ টেক্সট ফাইল অভিধানের মালিকানাটিতে মাথা নত করার জন্য একটি বিকৃত তাগিদ অনুভব করছি। যুক্তি যাই হোক না কেন, এটি
পাঠ্যত্বকে

51

সঙ্গে awkএটা প্রশংসনীয় দ্রুত:

awk 'NR == num_line' file

যখন এটি সত্য হয়, ডিফল্ট ব্যবহারকে awkহয় সঞ্চালিত: {print $0}


বিকল্প সংস্করণ

যদি আপনার ফাইলটি বিশাল আকার exitধারণ করে তবে প্রয়োজনীয় লাইনটি পড়ার পরে আপনি আরও ভাল । এইভাবে আপনি সিপিইউ সময় সাশ্রয় করুন উত্তরের শেষে সময়ের তুলনা দেখুন

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

আপনি যদি ব্যাশ ভেরিয়েবল থেকে লাইন নম্বর দিতে চান তবে আপনি এটি ব্যবহার করতে পারেন:

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 এস প্রায় 6x গুণ দ্রুত।


এই পদ্ধতিটি সর্বদা ধীর হতে চলেছে কারণ অ্যাভেক ফিল্ড বিভাজন করার চেষ্টা করে। ক্ষেত্র বিভাজনের ওভারহেড দ্বারা হ্রাস করা যেতে পারেawk 'BEGIN{FS=RS}(NR == num_line) {print; exit}' file
kvantour

আপনি যখন ফাইল 1 এর লাইন এন 1, ফাইল 2 এর এন 2, এন 3 বা ফাইল 3 ... সংযুক্ত করতে চান তখন এই পদ্ধতিতে অ্যাডকের আসল শক্তিটি সামনে আসে awk 'FNR==n' n=10 file1 n=30 file2 n=60 file3। জিএনইউ অ্যাজকের সাহায্যে এটি ব্যবহার করে গতি বাড়ানো যেতে পারে awk 'FNR==n{print;nextfile}' n=10 file1 n=30 file2 n=60 file3
কাওয়ানটোর

@ কেভান্টর সত্যই, এই জাতীয় জিনিসের জন্য জিএনইউ অ্যাডকের পরের ফাইলটি দুর্দান্ত। FS=RSক্ষেত্রের বিভাজন এড়ানো কীভাবে আসে ?
ফেডরকিই 'এসও ক্ষতিগ্রস্থ হওয়া বন্ধ করুন'

1
FS=RSক্ষেত্র বিভাজন এড়াতে না, কিন্তু এটি শুধুমাত্র $ 0 বেশী parses এবং শুধুমাত্র এক ক্ষেত্র নির্ধারণ নেই কারণ 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.3 জি ফাইল তৈরি করবে:

#!/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

আমার মেশিনে রান করার আউটপুট এখানে রয়েছে (একটি এসএসডি এবং 16 জি মেমরি সহ থিংকপ্যাড এক্স 1 কার্বন)। আমি ধরে নিয়েছি চূড়ান্ত রানটিতে সবকিছু ডিস্ক থেকে নয়, ক্যাশে থেকে আসবে:

*** 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
@ উইসবাকি আমার কোনও কঠোর পরিসংখ্যান নেই তবে প্রথমে লেজ ব্যবহারের একটি অসুবিধা "হেড -1" এর পরে আপনার মোট দৈর্ঘ্য আগে থেকেই জানা উচিত। যদি আপনি এটি না জানেন তবে আপনাকে প্রথমে এটি গণনা করতে হবে, এটি ক্ষতির পারফরম্যান্স অনুযায়ী হবে। আরেকটি অসুবিধা হ'ল এটি ব্যবহার করতে কম স্বজ্ঞাত। উদাহরণস্বরূপ, যদি আপনার 1 থেকে 10 নম্বর থাকে এবং আপনি 3 য় লাইন পেতে চান তবে আপনাকে "লেজ -8 | শীর্ষ -1" ব্যবহার করতে হবে। এটি "হেড -3 | লেজ -1" এর চেয়ে বেশি ত্রুটিযুক্ত।
ফিলিপ ক্লেন 21

দুঃখিত, আমি পরিষ্কার হতে একটি উদাহরণ অন্তর্ভুক্ত করা উচিত ছিল। head -5 | tail -1বনাম tail -n+5 | head -1। প্রকৃতপক্ষে, আমি আরও একটি উত্তর পেয়েছি যা একটি পরীক্ষার তুলনা করেছে এবং tail | headদ্রুত বলে মনে হয়েছে। stackoverflow.com/a/48189289
wisbucky

1
@ উইসবাকি এটি উল্লেখ করার জন্য আপনাকে ধন্যবাদ! আমি কিছু পরীক্ষা করেছিলাম এবং একমত হতে হয়েছিল যে আমি যা দেখলাম তার থেকে লাইনটির অবস্থানের চেয়ে এটি সর্বদা সামান্য দ্রুত ছিল। এটি দেওয়া, আমি আমার উত্তর পরিবর্তন করেছিলাম এবং কেউ যদি পুনরুত্পাদন করতে চায় সে ক্ষেত্রে মানদণ্ডও অন্তর্ভুক্ত করেছিলাম।
ফিলিপ ক্লেন

27

বাহ, সব সম্ভাবনা!

এটা চেষ্টা কর:

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

বা এর যে কোনও একটি আপনার আওক এর সংস্করণ অনুসারে:

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

এই প্রশ্নের হচ্ছে বাঁধা ব্যাশ, এখানে ব্যাশ (≥4) এরকম উপায়: ব্যবহার mapfileসঙ্গে -s(এড়ান) এবং -n(COUNT) বিকল্প।

আপনার যদি কোনও ফাইলের 42 তম লাইন পেতে হয় file:

mapfile -s 41 -n 1 ary < file

এই মুহুর্তে, আপনার একটি অ্যারে aryক্ষেত্র থাকবে যার রেখাগুলি file(পিছনের নিউলাইন সহ) থাকবে, যেখানে আমরা প্রথম 41 টি লাইন ( -s 41) এড়িয়ে গিয়েছি এবং একটি লাইন ( -n 1) পড়ার পরে থামিয়েছি । সুতরাং যে সত্যিই 42 তম লাইন। এটি মুদ্রণ করতে:

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

আপনার যদি অনেকগুলি লাইনের প্রয়োজন হয় তবে 42–666 (অন্তর্ভুক্ত) সীমাটি বলুন এবং বলুন যে আপনি নিজেই গণিতটি করতে চান না এবং স্টাডআউটে মুদ্রণ করুন:

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বিকল্প, ডিফল্ট অ্যাকশন যে লাইন প্রিন্ট করতে অক্ষম নিশ্চয় হিসাবে আপনি man পৃষ্ঠা এক ঝলকে দ্বারা দেখতে পেত।
ট্রিপল

ইন গনুহ sed সব sedউত্তর একই গতি চলেছেন। অতএব ( জিএনইউর পক্ষে sed ) এটি সেরা sedউত্তর, যেহেতু এটি বড় ফাইল এবং ছোট nth লাইনের মানগুলির জন্য সময় সাশ্রয় করে ।
এজিসি


6

বড় ফাইলগুলির দ্রুততম সমাধানটি সর্বদা লেজ | মস্তক হয়, তবে শর্ত থাকে যে দুটি দূরত্ব রয়েছে:

  • ফাইলের শুরু থেকে শুরু করার লাইনে। এটি কল করতে দেয়S
  • ফাইলের শেষ লাইন থেকে শেষের দূরত্ব। তা হওE

পরিচিত. তারপরে, আমরা এটি ব্যবহার করতে পারি:

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

হাউম্যানি হ'ল প্রয়োজনীয় লাইনগুলির গণনা।

Https://unix.stackexchange.com/a/216614/79743 এ আরও কিছু বিশদ


1
একক পরিষ্কার করে বলো Sএবং E(অর্থাত বাইট, অক্ষর, বা লাইন)।
এজিসি

6

উপরের সমস্ত উত্তর সরাসরি প্রশ্নের উত্তর দেয়। তবে চিন্তার উদ্রেক করার জন্য এখানে একটি কম সরাসরি সমাধান তবে সম্ভাব্য আরও গুরুত্বপূর্ণ ধারণা।

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

আসল সমাধানটি হ'ল একটি সূচক থাকে যেমন ফাইলের শুরুতে লাইনগুলি শুরু হয় এমন অবস্থানগুলি নির্দেশ করে। আপনি একটি ডাটাবেস ফর্ম্যাট ব্যবহার করতে পারেন, বা কেবল ফাইলের শুরুতে একটি টেবিল যুক্ত করতে পারেন। বিকল্পভাবে আপনার বড় পাঠ্য ফাইলের সাথে একটি পৃথক সূচি ফাইল তৈরি করুন।

যেমন আপনি নতুন লাইনের জন্য চরিত্রের অবস্থানের একটি তালিকা তৈরি করতে পারেন:

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-বাইট / মাল্টিবাইট অক্ষরগুলির সাথে কাজ নাও করতে পারে, যেহেতু অভ্যাসটি "চরিত্র-সচেতন" তবে লেজটি নয়।
  • আমি এটি কোনও বড় ফাইলের বিরুদ্ধে পরীক্ষা করিনি।
  • আরো দেখুন এই উত্তর
  • বিকল্পভাবে - আপনার ফাইলটিকে ছোট ফাইলগুলিতে ভাগ করুন!

5

ক্যাফেইনকনয়েসুরের খুব সহায়ক বেঞ্চমার্কিং উত্তরের অনুসরণ হিসাবে ... আমি জানতে আগ্রহী ছিলাম যে 'মানচিত্র ফাইল' পদ্ধতিটি অন্যদের সাথে কতটা দ্রুত তুলনা করা হয়েছিল (যেহেতু এটি পরীক্ষা করা হয়নি), তাই আমি নিজেকে দ্রুত এবং নোংরা গতির তুলনা হিসাবে চেষ্টা করেছি আমার কাছে 4 টি ব্যাশ আছে লোকেরা এর প্রশংসা গাইতে থাকায় আমি যখন ছিলাম, তখন উপরের উত্তরের একটি মন্তব্যে উল্লিখিত "লেজ | মাথা" পদ্ধতির (মাথা | পুচ্ছের পরিবর্তে) একটি পরীক্ষায় ছড়িয়ে পড়েছি। টেস্টফাইলে ব্যবহৃত আকারের প্রায় কিছুই আমার কাছে নেই; সংক্ষিপ্ত বিজ্ঞপ্তিতে আমি যে সেরাটি খুঁজে পেতে পারি তা হ'ল একটি 14 এম পেডিগ্রি ফাইল (লম্বা লাইনগুলি সাদা স্থান-বিচ্ছিন্ন, কেবল 12000 লাইনের নীচে)।

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

$ 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'ফাইলের 2 এবং 5 লাইন প্রদর্শন করবে। (তবে এটি অর্ডারটি সংরক্ষণ করবে না))
অ্যান্ড্রি মাকুখা

2

ভেরিয়েবলটি লাইন নম্বর হিসাবে ব্যবহার করে নবম লাইন প্রিন্ট করতে:

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

এখানে '-e' পতাকাটি কার্যকর করতে কমান্ডে স্ক্রিপ্ট যুক্ত করার জন্য রয়েছে।


2
কোলন একটি বাক্য গঠন ত্রুটি, এবং এটি একটি সেমিকোলন হওয়া উচিত।
ট্রিপলি

2

ইতিমধ্যে অনেক ভাল উত্তর। আমি ব্যক্তিগতভাবে বিশ্রী সঙ্গে যান। সুবিধার্থে, আপনি যদি ব্যাশ ব্যবহার করেন তবে কেবল নীচে নিজেরটি যুক্ত করুন ~/.bash_profile। এবং, পরের বার যখন আপনি লগ ইন করবেন (বা আপনি যদি এই আপডেটের পরে আপনার .bash_profile উত্স করে থাকেন), আপনার ফাইলগুলিকে পাইপ দেওয়ার জন্য আপনার কাছে একটি নতুন নিমফ্টি "nth" ফাংশন উপস্থিত থাকবে।

এটি কার্যকর করুন বা আপনার। / .Bash_profile (ব্যাশ ব্যবহার করে) এ রাখুন এবং ব্যাশটি আবার খুলুন (বা সম্পাদন করুন 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নামকরণ করতে পারেন (বা অন্য কোনও নাম যা আপনি পছন্দ করেন) to

#!/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

দায়িত্ববোধ উপভোগ করুন!

পি

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.