বাশ স্ক্রিপ্টে, কীভাবে স্ট্যান্ডআউট লাইন লাইন ক্যাপচার করবেন


26

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

# Start long command in a separated process and redirect stdout to temp file
longcommand > /tmp/tmp$$.out &

#loop until process completes
ps cax | grep longcommand > /dev/null
while [ $? -eq 0 ]
do
    #capture the last lines in temp file and determine if there is new content to analyse
    tail /tmp/tmp$$.out

    # ...

    sleep 1 s  # sleep in order not to clog cpu

    ps cax | grep longcommand > /dev/null
done

আমি এটি করতে একটি সহজ উপায় আছে কিনা তা জানতে চাই।

সম্পাদনা করুন:

আমার প্রশ্নটি পরিষ্কার করার জন্য, আমি এটি যুক্ত করব। longcommandলাইন দ্বারা প্রদর্শন এটির স্থিতি লাইন প্রতি সেকেন্ডে একবার। longcommandসম্পূর্ণ হওয়ার আগে আউটপুটটি ধরতে চাই ।

এইভাবে, longcommandআমি প্রত্যাশিত ফলাফলগুলি সরবরাহ না করলে আমি সম্ভাব্যরূপে হত্যা করতে পারি।

আমি চেষ্টা করেছি:

longcommand |
  while IFS= read -r line
  do
    whatever "$line"
  done

তবে whatever(উদাহরণস্বরূপ echo) longcommandসম্পূর্ণ হওয়ার পরে কেবল সম্পাদন করে ।


উত্তর:


32

কমান্ডটি কেবল একটি whileলুপে পাইপ করুন । এটির অনেকগুলি সূক্ষ্মতা রয়েছে তবে মূলত ( bashকোনও বা কোনও পসিক্স শেল):

longcommand |
  while IFS= read -r line
  do
    whatever "$line"
  done

এটির সাথে অন্য প্রধান গোটচা ( IFSনীচের জিনিসগুলি ব্যতীত ) হ'ল আপনি যখন লুপটি শেষ করে একবার ভেরিয়েবলগুলি ব্যবহার করার চেষ্টা করবেন। এর কারণ লুপটি আসলে একটি উপ-শেল (কেবল একটি অন্য শেল প্রক্রিয়া) এ কার্যকর করা হয় যা আপনি ভেরিয়েবলগুলি অ্যাক্সেস করতে পারবেন না (লুপটি যখন শেষ হয় তখন ভেরিয়েবলগুলি সম্পূর্ণরূপে চলে যায় this এটি ঘুরে দেখার জন্য, আপনি করতে পারেন:

longcommand | {
  while IFS= read -r line
  do
    whatever "$line"
    lastline="$line"
  done

  # This won't work without the braces.
  echo "The last line was: $lastline"
}

সেটিং Hauke উদাহরণ lastpipeমধ্যে bashআরেকটি সমাধান।

হালনাগাদ

আপনি যেমন কমান্ড আউটপুট প্রসেস করছেন তা নিশ্চিত করার জন্য, আপনি লাইন বাফার হতে stdbufপ্রসেসটি সেট করতে ব্যবহার করতে পারেন stdout

stdbuf -oL longcommand |
  while IFS= read -r line
  do
    whatever "$line"
  done

অভ্যন্তরীণভাবে আউটপুটটিকে ব্লকগুলিতে বাফার করার পরিবর্তে পাইপটিতে একবারে একটি লাইন লিখতে প্রক্রিয়াটি কনফিগার করবে। সতর্ক থাকুন যে প্রোগ্রামটি নিজেরাই অভ্যন্তরীণভাবে এই সেটিংটি পরিবর্তন করতে পারে। unbuffer(অংশ expect) বা এর সাথে একটি অনুরূপ প্রভাব অর্জন করা যেতে পারে script

stdbufজিএনইউ এবং ফ্রিবিএসডি সিস্টেমগুলিতে উপলভ্য, এটি কেবল stdioবাফারিংকে প্রভাবিত করে এবং কেবলমাত্র অ-সেটআপড, অ-সেটগিড অ্যাপ্লিকেশনগুলির জন্য কাজ করে যা গতিশীলভাবে সংযুক্ত রয়েছে (যেহেতু এটি একটি LD_PRELOAD কৌশল ব্যবহার করে)।


@ স্টাফেন এর IFS=দরকার নেই bash, আমি শেষবারের পরে এটি পরীক্ষা করেছিলাম।
গ্রামীণ

2
হ্যাঁ যদি আপনি বাদ পড়ে যান তবে এটির প্রয়োজন নেই line(যার ক্ষেত্রে ফলাফলটি $REPLYঅগ্রণী এবং পেছনের স্থানগুলি ছাঁটা ছাড়াই দেওয়া হয়)। চেষ্টা করুন: echo ' x ' | bash -c 'read line; echo "[$line]"'এবং সঙ্গে তুলনা echo ' x ' | bash -c 'IFS= read line; echo "[$line]"'বাecho ' x ' | bash -c 'read; echo "[$REPLY]"'
Stéphane Chazelas

@ স্টাফেন, ঠিক আছে, কখনই বুঝতে পারিনি যে এর মধ্যে একটি নামযুক্ত ভেরিয়েবলের মধ্যে পার্থক্য রয়েছে। ধন্যবাদ।
গ্রামীণ

@ গ্র্যামি আমার প্রশ্নে আমি পরিষ্কার হতে পারিনি তবে লংকম্যান্ডটি সম্পূর্ণ হওয়ার আগে আমি লাইন দিয়ে আউটপুট লাইনটি প্রক্রিয়া করতে চাই (লংকম্যান্ডগুলি ত্রুটির বার্তা প্রদর্শন করলে দ্রুত প্রতিক্রিয়া জানাতে)। আমি আমার প্রশ্নটি আরও পরিষ্কার করার জন্য সম্পাদনা করব
gfrigon

@gfrigon, আপডেট হয়েছে।
গ্রামীণ

2
#! /bin/bash
set +m # disable job control in order to allow lastpipe
shopt -s lastpipe
longcommand |
  while IFS= read -r line; do lines[i]="$line"; ((i++)); done
echo "${lines[1]}" # second line
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.