কিভাবে একটি ডিরেক্টরিতে 10 মিলিয়ন ফাইলের উপর সেড চালাতে হবে?


16

আমার একটি ডিরেক্টরি রয়েছে যা এতে 10144911 ফাইল রয়েছে। এখনও অবধি আমি নিম্নলিখিতটি চেষ্টা করেছি:

  • for f in ls; do sed -i -e 's/blah/blee/g' $f; done

আমার শেলটি lsবিধ্বস্ত হয়েছে , এটি একটি টিল্ডায় রয়েছে তবে কীভাবে এটি তৈরি করতে হয় তা আমি বুঝতে পারি না।

  • ls | xargs -0 sed -i -e 's/blah/blee/g'

এর জন্য অনেক বেশি আরগস sed

  • find . -name "*.txt" -exec sed -i -e 's/blah/blee/g' {} \;

আর কোনও স্মৃতি কাঁটাতে পারেনি

এই জাতীয় আদেশটি কীভাবে তৈরি করবেন সে সম্পর্কে অন্য কোনও ধারণা? ফাইলগুলির একে অপরের সাথে যোগাযোগের দরকার নেই। ls | wc -lমনে হচ্ছে (খুব ধীর) কাজ করছে তাই এটি অবশ্যই সম্ভব।


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

@ শানিত: ফাইলগুলিতে কিছু না করাও তত দ্রুত হবে ... সিরিয়াসলি? যদি আপনি ফাইলগুলির একটি সেটে কোনও প্যাটার্ন পরিবর্তন করতে চান তবে আপনাকে প্রতিটি ফাইলটি দেখতে হবে, যদি প্যাটার্ন রয়েছে কিনা। আপনি যদি আগে থেকেই জানেন যে আপনি 'কিছু' ফাইলগুলি এড়িয়ে যেতে পারেন তবে ফাইলগুলি স্পর্শ না করাও এটির দ্রুত স্পষ্ট। এবং প্রারম্ভকালীন সময়টি sedসম্ভবত আরম্ভের চেয়ে দ্রুত pythonবা perlতত দ্রুততর হয় , যদি আপনি সেই দোভাষীতে সবকিছু করেন তবে except
আকির

@ কীরা: আপনি কি বলছেন যে কমান্ড লাইনে যতগুলি ফাইল উপযুক্ত হবে তার জন্য একবার পার্ল বা পাইথন চালু করা সেই ফাইলগুলির প্রতিটির জন্য একবার সেড চালু করার চেয়ে ব্যয়বহুল? আমি যদি সত্যিই অবাক হতাম যদি তা হত। ------ আমি অনুমান আপনি বুঝতে পারে না যে আমার পরামর্শ হয় ডাকা (শুরু) এডিটিং প্রোগ্রাম একবার (অথবা অন্তত কম সময়ের - আমার উত্তর দেখুন), এবং এটি খোলার আছে, পরিবর্তন করুন এবং প্রতিটি ফাইলের resave পরিবর্তে those ফাইলগুলির প্রত্যেকের জন্য পৃথকভাবে সম্পাদনা প্রোগ্রামটি চাওয়ার চেয়ে।
intuited

আপনার প্রথম মন্তব্যটি আপনি কী বলতে চেয়েছিলেন তা প্রতিফলিত করে না: "পাইথন / পারল দ্বারা সেড প্রতিস্থাপন করুন" .. কেবলমাত্র তা করে এবং ওপেন যে কমান্ডলাইনটি দিয়েছেন তা দেখে, একজন নির্দোষ পাঠক ধরে নিতে পারেন যে "সন্ধান করুন। এক্সটিক পাইথন" "ফাইন্ড।-এক্সেক সেড" এর চেয়ে দ্রুত .. এটি স্পষ্টতই কেস নয়। আপনার নিজের জবাবটিতে আপনি পাইথনটিকে প্রকৃত প্রয়োজনের চেয়ে অনেক বেশি কল করেন।
আকিরা

আমি মনে করি যে আকিরা আপনার (অন্তর্নিহিত) পরামর্শটি ভুল ব্যাখ্যা করেছে। আমি বিশ্বাস করি আপনি একসাথে ফাইলগুলি গুচ্ছ করার পরামর্শ দিচ্ছিলেন। আমি আমার xargs প্রয়াস দিয়ে চেষ্টা করেছি, আবার চেষ্টা করার সময় :) :)
সান্দ্রো

উত্তর:


19

একবার চেষ্টা করে দেখুন:

find -name '*.txt' -print0 | xargs -0 -I {} -P 0 sed -i -e 's/blah/blee/g' {}

এটির প্রতিটি অনুরোধে কেবল একটি ফাইলের নাম খাওয়ানো হবে sed। এটি "শেডের জন্য অনেকগুলি আর্গুমেন্ট" সমস্যার সমাধান করবে। -Pবিকল্প একাধিক প্রসেস একই সময়ে forked করার অনুমতি দিতে হবে। যদি 0 কাজ না করে (এটি যতটা সম্ভব চালানো উচিত), সংখ্যাটি সীমাবদ্ধ করতে অন্যান্য সংখ্যা (10? 100? আপনার সংখ্যার কোর সংখ্যা?) চেষ্টা করুন।


3
সম্ভবত, এটা হতে প্রয়োজন হবে find . -name \*.txt -print0শেল উল্লিখিত glob প্রসারিত হচ্ছে এবং 10 মিলিয়ন আর্গুমেন্ট করার জন্য alloc স্থান বের করার চেষ্টা এড়াতে এটি
ক্রিস জনসন

@ ক্রিস জোহেনসেন: হ্যাঁ, এটা ঠিক। আমি আমার উত্তর পোস্ট করতে ছুটে এসেছি এবং সেই প্রয়োজনীয় অংশগুলি সহ মিস করেছি। আমি এই সংশোধনগুলি দিয়ে আমার উত্তর সম্পাদনা করেছি। ধন্যবাদ।
পরবর্তী বিজ্ঞপ্তি না দেওয়া পর্যন্ত বিরতি দেওয়া হয়েছে।

এখনই এটি চেষ্টা করে ... আঙ্গুলগুলি অতিক্রম করে
সান্দ্রো

7

আমি 10 এ এই পদ্ধতিটি পরীক্ষা করেছি (এবং অন্যান্য সমস্ত) মিলিয়ন (খালি) ফাইলে পরীক্ষা করেছি, "হ্যালো 00000001" থেকে "হ্যালো 10000000" (নাম অনুসারে 14 বাইট) দেওয়া হয়েছে।

আপডেট: আমি এখন পদ্ধতিতে একটি কোয়াড-কোর রান অন্তর্ভুক্ত করেছি 'find |xargs'(এখনও 'সেড' ছাড়াই; কেবল প্রতিধ্বনি> / দেব / নাল) ..

# Step 1. Build an array for 10 million files
#   * RAM usage approx:  1.5 GiB 
#   * Elapsed Time:  2 min 29 sec 
  names=( hello\ * )

# Step 2. Process the array.
#   * Elapsed Time:  7 min 43 sec
  for (( ix=0, cnt=${#names[@]} ; ix<$cnt; ix++ )) ; do echo "${names[ix]}" >/dev/null ; done  

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

ডেনিসের 'find |xargs'পদ্ধতিটি, একটি একক কোর ব্যবহার করে, * রান করার bash arrayপদ্ধতির চেয়ে * 4 ঘন্টা 21 মিনিট ** বেশি সময় নেয় no sed... তবে, 'সন্ধান' দ্বারা প্রদত্ত মাল্টি-কোর সুবিধাটি যখন সময়ের জন্য আহ্বান করা হচ্ছে তখন দেখানো সময়ের পার্থক্য ছাড়িয়ে যাবে ফাইলগুলি প্রক্রিয়া করা হচ্ছে ...

           | Time    | RAM GiB | Per loop action(s). / The command line. / Notes
-----------+---------+---------+----------------------------------------------------- 
Dennis     | 271 min | 1.7 GiB | * echo FILENAME >/dev/null
Williamson   cores: 1x2.66 MHz | $ time find -name 'hello *' -print0 | xargs -0 -I {} echo >/dev/null {}
                               | Note: I'm very surprised at how long this took to run the 10 million file gauntlet
                               |       It started processing almost immediately (because of xargs I suppose),  
                               |       but it runs **significantly slower** than the only other working answer  
                               |       (again, probably because of xargs) , but if the multi-core feature works  
                               |       and I would think that it does, then it could make up the defecit in a 'sed' run.   
           |  76 min | 1.7 GiB | * echo FILENAME >/dev/null
             cores: 4x2.66 MHz | $ time find -name 'hello *' -print0 | xargs -0 -I {} -P 0 echo >/dev/null {}
                               |  
-----------+---------+---------+----------------------------------------------------- 
fred.bear  | 10m 12s | 1.5 GiB | * echo FILENAME >/dev/null
                               | $ time names=( hello\ * ) ; time for (( ix=0, cnt=${#names[@]} ; ix<$cnt; ix++ )) ; do echo "${names[ix]}" >/dev/null ; done
-----------+---------+---------+----------------------------------------------------- 
l0b0       | ?@#!!#  | 1.7 GiB | * echo FILENAME >/dev/null 
                               | $ time  while IFS= read -rd $'\0' path ; do echo "$path" >/dev/null ; done < <( find "$HOME/junkd" -type f -print0 )
                               | Note: It started processing filenames after 7 minutes.. at this point it  
                               |       started lots of disk thrashing.  'find' was using a lot of memory, 
                               |       but in its basic form, there was no obvious advantage... 
                               |       I pulled the plug after 20 minutes.. (my poor disk drive :(
-----------+---------+---------+----------------------------------------------------- 
intuited   | ?@#!!#  |         | * print line (to see when it actually starts processing, but it never got there!)
                               | $ ls -f hello * | xargs python -c '
                               |   import fileinput
                               |   for line in fileinput.input(inplace=True):
                               |       print line ' 
                               | Note: It failed at 11 min and approx 0.9 Gib
                               |       ERROR message: bash: /bin/ls: Argument list too long  
-----------+---------+---------+----------------------------------------------------- 
Reuben L.  | ?@#!!#  |         | * One var assignment per file
                               | $ ls | while read file; do x="$file" ; done 
                               | Note: It bombed out after 6min 44sec and approx 0.8 GiB
                               |       ERROR message: ls: memory exhausted
-----------+---------+---------+----------------------------------------------------- 

2

সম্পূর্ণ নিরাপদ সন্ধানের জন্য আর একটি সুযোগ :

while IFS= read -rd $'\0' path
do
    file_path="$(readlink -fn -- "$path"; echo x)"
    file_path="${file_path%x}"
    sed -i -e 's/blah/blee/g' -- "$file_path"
done < <( find "$absolute_dir_path" -type f -print0 )

1

এটি বেশিরভাগ ক্ষেত্রে অফ-টপিক, তবে আপনি ব্যবহার করতে পারেন

find -maxdepth 1 -type f -name '*.txt' | xargs python -c '
import fileinput
for line in fileinput.input(inplace=True):
    print line.replace("blah", "blee"),
'

এখানে মূল উপকারটি (ওভার ... xargs ... -I {} ... sed ...) গতি হ'ল: আপনি sed১ কোটি বার আহ্বান করা এড়াবেন । এটি দ্রুততর হবে যদি আপনি পাইথন ব্যবহার করা এড়াতে পারেন (যেহেতু পাইথন ধীর ধরণের, তুলনামূলকভাবে), তাই এই কাজের জন্য পার্ল আরও ভাল পছন্দ হতে পারে। পার্ল দিয়ে কীভাবে সমতুল্য সুবিধে করা যায় তা আমি নিশ্চিত নই।

এটি যেভাবে কাজ করে তা হ'ল xargsপাইথনকে যতটা যুক্তি যুক্ত হয় যা এটি একটি একক কমান্ড লাইনে ফিট করতে পারে এবং ততক্ষণ ততক্ষণ তা চালিয়ে যাবেন যতক্ষণ না এটি আর্গুমেন্টের বাইরে চলে যায় (যা সরবরাহ করা হচ্ছে ls -f *.txt)। প্রতিটি অনুরোধে আর্গুমেন্টের সংখ্যা ফাইলের দৈর্ঘ্যের উপর নির্ভর করবে এবং, কিছু অন্যান্য স্টাফ। fileinput.inputফাংশন প্রতিটি আবাহন এর আর্গুমেন্ট নাম ফাইল থেকে ধারাবাহিক লাইন উৎপাদ, এবংinplace বিকল্প যে এটি জাদুর "ধরা" আউটপুট এবং এটি ব্যবহার প্রতিটি লাইনে প্রতিস্থাপন করতে বলে।

দ্রষ্টব্য যে পাইথনের স্ট্রিং replaceপদ্ধতিটি রিজেক্সস ব্যবহার করে না; আপনার যদি এগুলির দরকার হয় তবে আপনার import reব্যবহার ও ব্যবহার করতে হবে print re.sub(line, "blah", "blee")। এগুলি হ'ল পার্ল-সামঞ্জস্যপূর্ণ রেজিএক্সপস, যা আপনি পেয়ে যাচ্ছেন তার ভারী মজবুত সংস্করণ sed -r

সম্পাদন করা

আকিরা মন্তব্যে যেমন উল্লেখ করেছেন, কমান্ডের ls -f *.txtজায়গায় একটি findগ্লোব ( ) ব্যবহার করে আসল সংস্করণটি কাজ করবে না কারণ গ্লোবগুলি bashনিজেই শেল ( ) দ্বারা প্রক্রিয়াজাত হয় । এর অর্থ হ'ল কমান্ডটি চালানোর আগে, 10 মিলিয়ন ফাইলের নাম কমান্ড লাইনে প্রতিস্থাপন করা হবে। এটি একটি কমান্ডের আর্গুমেন্ট তালিকার সর্বাধিক আকার ছাড়িয়ে যাওয়ার গ্যারান্টিযুক্ত। আপনি এটিতে xargs --show-limitsসিস্টেম-নির্দিষ্ট তথ্যের জন্য ব্যবহার করতে পারেন ।

আর্গুমেন্ট তালিকার সর্বাধিক আকারটিও আমলে নেওয়া হয় xargs, যা সেই সীমা অনুসারে অজগরটির প্রতিটি অনুরোধে যে আর্গুমেন্টগুলি পাস করে তা সীমাবদ্ধ করে। যেহেতু xargsএখনও বেশ কয়েকবার অজগরকে ডাকতে os.path.walkহবে, ফাইল তালিকা পেতে আকিরের পরামর্শটি আপনাকে সম্ভবত কিছুটা সময় সাশ্রয় করবে।


1
গ্লোব অপারেটরটি ব্যবহার করার কী দরকার (যা যাইহোক এটি অনেকগুলি ফাইলের জন্য ব্যর্থ হবে) ... এবং তারপরে অজগরকে ফাইলগুলি খাওয়ান os.path.walk()?
আকির

@akira: উল্লিখিত glob অপারেটর সামগ্রীগুলি প্রতিস্থাপন করতে চেষ্টা এড়াতে হয় .এবং ..। অবশ্যই এটি করার অন্যান্য উপায় রয়েছে (অর্থাত্ find) তবে আমি ওপি যা বোঝে তার সাথে যতটা সম্ভব ঘনিষ্ঠভাবে চেষ্টা করার চেষ্টা করছি। এটি ব্যবহার না করার কারণও os.path.walk
intuited

@ ককিরা: ভাল পরামর্শ, যদিও এটি সম্ভবত যথেষ্ট দ্রুত হবে।
intuited

আমি মনে করি যে ওপি os.path.walkবেশ সহজেই বুঝতে পারবে।
আকিরা

0

চেষ্টা করুন:

ls | while read file; do (something to $file); done

2
ls -fভালো হবে; আপনি কি সত্যিই এটির জন্য অপেক্ষা করতে stat()এবং এতগুলি ফাইল বাছাই করতে চান?
গাইকোসৌর

এই মুহূর্তে আমি চেষ্টা করছি: জন্য f। * .txt; বালা; সম্পন্ন. যদি এটি ব্যর্থ হয় তবে আমি একটি ঘৃণ্য দেব। ধন্যবাদ!
সান্দ্রো
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.