বাশ স্ক্রিপ্টিং এবং বড় ফাইল (বাগ): পুনঃনির্দেশ থেকে রিড বিল্টিনের সাথে ইনপুটটি অপ্রত্যাশিত ফলাফল দেয়


16

বড় ফাইল এবং এর সাথে আমার একটি অদ্ভুত সমস্যা রয়েছে bash। এটি প্রসঙ্গে:

  • আমার কাছে একটি বড় ফাইল রয়েছে: 75 জি এবং 400,000,000+ লাইন (এটি একটি লগ ফাইল, আমার খারাপ, আমি এটি বাড়িয়ে তুলি)।
  • প্রতিটি লাইনের প্রথম 10 টি অক্ষর YYYY-MM-DD বিন্যাসে একটি সময় স্ট্যাম্প।
  • আমি সেই ফাইলটি বিভক্ত করতে চাই: প্রতিদিন একটি ফাইল।

আমি নিম্নলিখিত স্ক্রিপ্ট দিয়ে চেষ্টা করেছি যা কার্যকর হয়নি। আমার প্রশ্ন সম্পর্কে কাজ করছে না এই স্ক্রিপ্টের না বিকল্প সমাধান হয়

while read line; do
  new_file=${line:0:10}_file.log
  echo "$line" >> $new_file
done < file.log

ডিবাগিংয়ের পরে, আমি new_fileভেরিয়েবলটিতে সমস্যাটি পেয়েছি । এই লিপি:

while read line; do
  new_file=${line:0:10}_file.log
  echo $new_file
done < file.log | uniq -c

ফলাফলটি নমুনা দেয় (আমি xডেটা গোপনীয় রাখার জন্য এসএসটি রাখি, অন্যান্য অক্ষরগুলিই আসল। dhএবং আরও ছোট স্ট্রিং লক্ষ্য করুন :

...
  27402 2011-xx-x4
  27262 2011-xx-x5
  22514 2011-xx-x6
  17908 2011-xx-x7
...
3227382 2011-xx-x9
4474604 2011-xx-x0
1557680 2011-xx-x1
      1 2011-xx-x2
      3 2011-xx-x1
...
     12 2011-xx-x1
      1 2011-xx-dh
      1 2011-xx-x1
      1 208--
      1 2011-xx-x1
      1 2011-xx-dh
      1 2011-xx-x1    
...

এটি আমার ফাইলের ফর্ম্যাটে কোনও সমস্যা নয় । স্ক্রিপ্টটি cut -c 1-10 file.log | uniq -cকেবল বৈধ সময় স্ট্যাম্প দেয়। মজার বিষয় হল, উপরের আউটপুটটির একটি অংশটি এর সাথে হয়ে যায় cut ... | uniq -c:

3227382 2011-xx-x9
4474604 2011-xx-x0
5722027 2011-xx-x1

আমরা দেখতে পাব যে ইউনিট গণনার পরে 4474604আমার প্রাথমিক স্ক্রিপ্টটি ব্যর্থ হয়েছে।

আমি কি জানি না এমন ব্যাশে এমন একটি সীমাবদ্ধতা ফেলেছিলাম যা আমি জানি না, আমি কি ব্যাশে কোনও বাগ পেয়েছি (এটি অসম্ভব সম্ভাবনাময়), বা আমি কোনও ভুল করেছি?

আপডেট :

ফাইলটির 2 জি পড়ার পরে সমস্যাটি ঘটে। এটি seams readএবং পুনঃনির্দেশ 2G এর চেয়ে বড় ফাইল পছন্দ করে না। তবে এখনও আরও সুনির্দিষ্ট ব্যাখ্যা অনুসন্ধান করা হচ্ছে।

আপডেট 2 :

এটি অবশ্যই বাগের মতো দেখাচ্ছে। এটি দিয়ে পুনরুত্পাদন করা যেতে পারে:

yes "0123456789abcdefghijklmnopqrs" | head -n 100000000 > file
while read line; do file=${line:0:10}; echo $file; done < file | uniq -c

তবে এটি একটি পরিশ্রমী হিসাবে সূক্ষ্মভাবে কাজ করে (এটি সিম যে আমি একটি দরকারী ব্যবহার পেয়েছি cat):

cat file | while read line; do file=${line:0:10}; echo $file; done | uniq -c 

জিএনইউ এবং দেবিয়ানকে একটি বাগ দায়ের করা হয়েছে। আক্রান্ত সংস্করণগুলি bashডেবিয়ান স্কুইজ 6.0.2 এবং 6.0.4 এ 4.1.5 .4

echo ${BASH_VERSINFO[@]}
4 1 5 1 release x86_64-pc-linux-gnu

Update3:

আমার বাগ রিপোর্টটি সম্পর্কে দ্রুত প্রতিক্রিয়া জানিয়েছিলেন আন্ড্রেয়স সোয়াবকে ধন্যবাদ, এই প্যাচই এই আচরণের সমাধান। lib/sh/zread.cগিলস যত তাড়াতাড়ি দেখিয়েছে তেমন প্রভাবিত ফাইলটি :

diff --git a/lib/sh/zread.c b/lib/sh/zread.c index 0fd1199..3731a41 100644
--- a/lib/sh/zread.c
+++ b/lib/sh/zread.c @@ -161,7 +161,7 @@ zsyncfd (fd)
      int fd; {   off_t off;
-  int r;
+  off_t r;

  off = lused - lind;   r = 0;

rপরিবর্তনশীল ফেরত মূল্য রাখা ব্যবহার করা হয় lseek। হিসাবে lseekআয় ফাইলের শুরু থেকে অফসেট, যখন এটি 2GB শেষ হলে, intমান নেতিবাচক, যা পরীক্ষা ঘটে if (r >= 0)ব্যর্থ যেখানে এটি সফল থাকা উচিত।


1
আপনি কি ইনপুট ডেটার ছোট সেট দিয়ে সমস্যার প্রতিলিপি করতে পারেন? এই সমস্যাগুলির ফলে কি সর্বদা একই ইনপুট লাইন থাকে?
লারস্ক করে

@ আলার্কস: ভাল প্রশ্ন। সমস্যাটি সর্বদা # 13.520.918 লাইনে শুরু হয় (আমি যা পরীক্ষাগুলি দিয়েছিলাম তার জন্য দু'বার) এই লাইনের আগে ফাইলটির আকার 2.147.487.726। এটি সীমাবদ্ধ করে তোলে যে এখানে 32 বিটস সীমা রয়েছে, তবে ঠিক যেমন আমরা 2 ^ 31 (2.147.483.648) এর চেয়ে সামান্য, কিন্তু ঠিক 4K বাফার সীমাতে (2 ^ 31 + 4K = 2.147.487.744)। পূর্ববর্তী এবং পরবর্তী লাইনগুলি সাধারণ 100 থেকে 200 অক্ষরের লাইন।
jfg956

২ য় ফাইলে (প্রায় একই আকারের) পরীক্ষিত: সমস্যাটি # 13.522.712 লাইনে শুরু হয় এবং ফাইলটি লাইনটির আগে 2.147.498.679 বাইট বড়। এটি ব্যাশের readবিবৃতিতে সীমাবদ্ধতার দিক নির্দেশ করতে seams করে ।
jfg956

উত্তর:


13

আপনি এক প্রকারের ব্যাশে খুঁজে পেয়েছেন। এটি একটি পরিচিত ফিক্স সহ একটি পরিচিত বাগ।

প্রোগ্রামগুলি একটি সীমাবদ্ধ আকারের সাথে কিছু পূর্ণসংখ্যার ধরণের ভেরিয়েবল হিসাবে একটি ফাইলের একটি অফসেট উপস্থাপন করে। পুরানো দিনগুলিতে সকলেই intপ্রায় সমস্ত কিছুর জন্য ব্যবহার করতেন , এবং intপ্রকারটি সাইন বিট সহ 32 বিটের মধ্যে সীমাবদ্ধ ছিল, তাই এটি -2147483648 থেকে 2147483647 পর্যন্ত মান সংরক্ষণ করতে পারে Now আজকাল বিভিন্ন জিনিসের জন্য বিভিন্ন ধরণের নাম রয়েছে যার মধ্যে off_tএকটি রয়েছে একটি ফাইল অফসেট।

ডিফল্টরূপে, off_t32-বিট প্ল্যাটফর্মের একটি 32-বিট টাইপ (2 গিগাবাইট পর্যন্ত মঞ্জুরি দেওয়া) এবং -৪-বিট প্ল্যাটফর্মের উপর একটি -৪-বিট টাইপ (8EB পর্যন্ত অনুমতি দেওয়া হয়)। যাইহোক, LARGEFILE বিকল্পের সাথে প্রোগ্রামগুলি সংকলন করা সাধারণ, যা প্রকারটি 64৪ off_tবিট প্রস্থের দিকে স্যুইচ করে এবং প্রোগ্রামটিকে ফাংশনগুলির যথাযথ প্রয়োগকরণকে উপযুক্ত করে তোলে lseek

দেখা যাচ্ছে যে আপনি 32-বিট প্ল্যাটফর্মে বাশ চালাচ্ছেন এবং আপনার ব্যাশ বাইনারি বড় ফাইল সমর্থন সহ সংকলিত নেই। এখন, আপনি যখন নিয়মিত ফাইল থেকে একটি লাইন পড়েন, তখন পারফরম্যান্সের জন্য ব্যাচগুলিতে অক্ষরগুলি পড়তে বাশ একটি অভ্যন্তরীণ বাফার ব্যবহার করে (আরও তথ্যের জন্য, উত্সটি দেখুন builtins/read.def)। লাইনটি সম্পূর্ণ হয়ে গেলে, বাশ কল lseekকরে যে ফাইলটি অফসেটটিকে লাইনের শেষের অবস্থানে ফিরিয়ে আনবে, যদি অন্য কোনও প্রোগ্রাম সেই ফাইলটির অবস্থানের বিষয়ে যত্নশীল হয়। থেকে কল lseekঘটবে zsyncfcফাংশন lib/sh/zread.c

উত্সটি আমি খুব বেশি বিশদে পড়ি নি, তবে আমি দৃm়ভাবে মনে করি যে যখন পরিপূর্ণ অফসেটটি নেতিবাচক হয় তখন রূপান্তরের পর্যায়ে কিছু সহজেই ঘটছে না। সুতরাং ব্যাশটি 2 জিবি নম্বর পাস করার পরে এটি তার বাফারটি পুনরায় পূরণ করার পরে ভুল অফসেটগুলিতে পড়া শেষ করে।

যদি আমার উপসংহারটি ভুল হয় এবং আপনার বাশ আসলে 64৪-বিট প্ল্যাটফর্মে চলতে থাকে বা লজফিল সমর্থন সহ সংকলিত হয়, এটি অবশ্যই একটি বাগ। আপনার বিতরণ বা প্রবাহে দয়া করে এটি প্রতিবেদন করুন ।

যে কোনও উপায়ে এত বড় ফাইলগুলি প্রক্রিয়া করার জন্য একটি শেলই সঠিক সরঞ্জাম নয়। এটা ধীর হতে যাচ্ছে। সম্ভব হলে সেড ব্যবহার করুন, অন্যথায় অজানা।


1
মারসি গিলস দুর্দান্ত উত্তর: শক্তিশালী সিএস ব্যাকগ্রাউন্ড (32 বিট ...) না থাকা লোকদের এমনকি সমস্যাটি বোঝার জন্য পর্যাপ্ত তথ্য সহ সম্পূর্ণ। (লারস্কগুলি লাইন নম্বরটিতে প্রশ্ন জিজ্ঞাসা করতেও সহায়তা করে এবং এটি স্বীকার করা উচিত)) এর পরে, আমি যদিও একটি 32 বিট সমস্যা এবং উত্সটি ডাউনলোড করেছি, তবে এখনও বিশ্লেষণের এই স্তরে ছিলাম না। Merci এনকোরি, এবং জার্নি।
jfg956

4

আমি ভুল সম্পর্কে জানি না, তবে এটি অবশ্যই সংশ্লেষিত। যদি আপনার ইনপুট লাইনগুলি দেখতে এই রকম হয়:

YYYY-MM-DD some text ...

তাহলে এর আসলে কোনও কারণ নেই:

new_file=${line:0:4}-${line:5:2}-${line:8:2}_file.log

আপনি এমন কিছু দেখতে পেল যা প্রচুর সাবস্ট্রিংয়ের কাজ করে যা দেখে মনে হচ্ছে ... ঠিক আগের মতো ফাইলটিতে এটি দেখায়। এ কেমন?

while read line; do
  new_file="${line:0:10}_file.log"
  echo "$line" >> $new_file
done

এটি লাইনটি থেকে প্রথম 10 টি অক্ষর ধরে। আপনি bashসম্পূর্ণরূপে এবং কেবল ব্যবহারের মাধ্যমেও সরবরাহ করতে পারেন awk:

awk '{print > ($1 "_file.log")}' < file.log

এটি তারিখটি ধরে রাখে $1(প্রতিটি লাইনে প্রথম সাদা স্থান-বিস্মৃত কলাম) এবং এটি ফাইলের নাম তৈরি করতে ব্যবহার করে।

মনে রাখবেন যে আপনার ফাইলগুলিতে কিছু বগাস লগ লাইন রয়েছে। অর্থাৎ সমস্যাটি আপনার স্ক্রিপ্ট নয়, ইনপুট নিয়ে থাকতে পারে be আপনি awkস্ক্রিপ্টটি বগাস লাইনে ফ্ল্যাগের মতো প্রসারিত করতে পারেন:

awk '
$1 ~ /[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/ {
    print > ($1 "_file.log")
    next
}

{
    print "INVALID:", $0
}
'

এই লাইন মেলে লিখুন YYYY-MM-DD আপনার লগ ফাইলগুলির সাথে এবং ফ্ল্যাগ লাইনগুলি স্ট্যাডআউটে কোনও টাইমস্ট্যাম্প দিয়ে শুরু করে না


আমার ফাইলে কোনও বোগাস লাইন নেই: cut -c 1-10 file.log | uniq -cআমাকে প্রত্যাশিত ফলাফল দেয়। আমি ব্যবহার করছি ${line:0:4}-${line:5:2}-${line:8:2}কারণ আমি ফাইলটি একটি ডিরেক্টরিতে রেখে ${line:0:4}/${line:5:2}/${line:8:2}দেব এবং আমি সমস্যাটি সরল করে দিয়েছি (আমি সমস্যার বিবৃতিটি আপডেট করব)। আমি জানি awkএখানে আমাকে সহায়তা করতে পারে তবে আমি এটি ব্যবহার করে অন্যান্য সমস্যায় পড়েছি। আমি যা চাই তা হ'ল সমস্যাটি বোঝা bash, বিকল্প সমাধানের সন্ধান না করা।
jfg956

যেমনটি আপনি বলেছেন ... আপনি যদি প্রশ্নটিতে সমস্যাটিকে "সরল" করেন, আপনি সম্ভবত উত্তরগুলি চান তা পাচ্ছেন না। আমি এখনও মনে করি বাশ দিয়ে এটি সমাধান করা এই ধরণের ডেটা প্রক্রিয়া করার পক্ষে সঠিক উপায় নয় তবে এটি কাজ না করার কোনও কারণ নেই।
লারস্ক করে

সরলিকৃত সমস্যাটি প্রশ্নটিতে আমি যে অপ্রত্যাশিত ফলাফলটি দিয়েছি তা দেয়, সুতরাং আমি মনে করি না যে এটি একটি ওভারসিম্প্লিফিকেশন। তদুপরি, সরলীকৃত সমস্যা cutকাজ করে এমন বিবৃতি হিসাবে একই ফলাফল দেয় । আমি যেমন আপেলকে কমলার সাথে নয়, আপেলের সাথে তুলনা করতে চাই, আমার প্রয়োজনীয় জিনিসগুলি যথাসম্ভব সাদৃশ্য করা দরকার।
jfg956

1
আমি আপনাকে একটি প্রশ্ন রেখেছি যা হয়ত জিনিসগুলি কোথায় খারাপ হচ্ছে তা নির্ধারণে সহায়তা করতে পারে ...
21

2

আপনি যা করতে চান তা মনে হচ্ছে:

awk '
{  filename = substr($0, 0, 10) "_file.log";  # input format same as output format
   if (filename != lastfile) {
       close(lastfile);
       print 'finished writing to', lastfile;
   }
   print >> filename;
   lastfile=filename;
}' file.log

closeপূরণ থেকে খোলা ফাইল টেবিল রাখে।


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