শেল পাইপে ত্রুটি কোডগুলি ধরা হচ্ছে


98

আমার কাছে বর্তমানে একটি স্ক্রিপ্ট রয়েছে যা এর মতো কিছু করে

./a | ./b | ./c

আমি এটিকে সংশোধন করতে চাই যাতে একটি, বি বা সি এর মধ্যে যদি কোনও ত্রুটি কোড সহ প্রস্থান করে থাকে তবে আমি একটি ত্রুটি বার্তা প্রিন্ট করব এবং খারাপ আউটপুট এগিয়ে রাখার পরিবর্তে থামিয়ে দেব।

এটি করার সহজতম / পরিষ্কার উপায় কী হবে?


7
সত্যিই এমন কিছু হওয়া দরকার &&|যার অর্থ "পূর্ববর্তী কমান্ডটি সফল হলে কেবল পাইপটি চালিয়ে যান"। আমি মনে করি আপনিও থাকতে পারেন |||যার অর্থ "পূর্ববর্তী কমান্ড ব্যর্থ হলে পাইপ চালিয়ে যান" (এবং সম্ভবত বাশ 4 এর মতো ত্রুটি বার্তাটি পাইপ করা হবে |&)।
পরবর্তী বিজ্ঞপ্তি না হওয়া পর্যন্ত বিরতি দেওয়া হয়েছে।

6
@DennisWilliamson, তাই না "নল করিব 'করতে পারবেন না কারণ a, b, cকমান্ড ক্রমানুসারে কিন্তু সমান্তরাল চালাতে না হয়। অন্য কথায়, ডেটা ক্রমান্বয়ে থেকে প্রবাহিত aহয় cতবে প্রকৃত a, bএবং cআদেশগুলি একই সাথে শুরু হয় (মোটামুটি)।
গিয়াকোমো

উত্তর:


20

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

tmp=${TMPDIR:-/tmp}/mine.$$
if ./a > $tmp.1
then
    if ./b <$tmp.1 >$tmp.2
    then
        if ./c <$tmp.2
        then : OK
        else echo "./c failed" 1>&2
        fi
    else echo "./b failed" 1>&2
    fi
else echo "./a failed" 1>&2
fi
rm -f $tmp.[12]

'1> & 2' পুনঃনির্দেশটি সংক্ষেপে '> & 2'ও করা যেতে পারে; তবে, এমকেএস শেলের একটি পুরানো সংস্করণ পূর্ববর্তী '1' ছাড়াই ত্রুটি পুনর্নির্দেশটিকে ভুলভাবে ছড়িয়ে দিয়েছে তাই আমি বয়সের জন্য নির্ভরযোগ্যতার জন্য সেই দ্ব্যর্থহীন স্বরলিপিটি ব্যবহার করেছি।

আপনি কিছু বাধা দিলে ফাইলগুলি ফাঁস হয়। বোমা-প্রমাণ (আরও কম) শেল প্রোগ্রামিং ব্যবহার করে:

tmp=${TMPDIR:-/tmp}/mine.$$
trap 'rm -f $tmp.[12]; exit 1' 0 1 2 3 13 15
...if statement as before...
rm -f $tmp.[12]
trap 0 1 2 3 13 15

প্রথম ট্র্যাপ লাইনটি 'কমান্ডগুলি চালান' rm -f $tmp.[12]; exit 1বলে যখন 1 সিগন্যাল, 2 স্বাক্ষর, 3 স্বাক্ষর, 13 সাইনপিপ, বা 15 সাইনটারম ঘটে, বা 0 (যখন শেলটি কোনও কারণে প্রস্থানিত হয়)। আপনি যদি একটি শেল স্ক্রিপ্ট লিখছেন তবে চূড়ান্ত ফাঁদটি কেবল 0-তে ফাঁদটি সরিয়ে ফেলতে হবে, এটি শেল প্রস্থান ট্র্যাপ (প্রক্রিয়াটি যেভাবেই শেষ হতে চলেছে বলে আপনি অন্যান্য সংকেতগুলিকে জায়গায় রেখে দিতে পারেন)।

মূল পাইপলাইনে, 'গ' এর পক্ষে 'এ' শেষ হওয়ার আগে 'বি' থেকে ডেটা পড়া সম্ভব হবে - এটি সাধারণত কাম্য হয় (উদাহরণস্বরূপ এটি একাধিক কোরকে কাজ করতে দেয়)। যদি 'বি' একটি 'সাজান' পর্যায়ে থাকে, তবে এটি প্রযোজ্য নয় - 'বি' এর কোনও আউটপুট জেনারেট করার আগে তার সমস্ত ইনপুট দেখতে হবে।

কোন কমান্ড (গুলি) ব্যর্থ হয়েছে তা আপনি যদি সনাক্ত করতে চান তবে আপনি এটি ব্যবহার করতে পারেন:

(./a || echo "./a exited with $?" 1>&2) |
(./b || echo "./b exited with $?" 1>&2) |
(./c || echo "./c exited with $?" 1>&2)

এটি সহজ এবং প্রতিসম: এটি 4-অংশ বা এন-অংশ পাইপলাইন পর্যন্ত প্রসারিত করা তুচ্ছ।

'সেট-ই' দিয়ে সাধারণ পরীক্ষা-নিরীক্ষা সাহায্য করে না।



@ ডেনিস: হ্যাঁ, আমি মনে করি আমার এমকেটেম্প বা টেম্পফিলের মতো কমান্ড ব্যবহার করা উচিত; ওহ এত বছর আগে যখন আমি এটি শিখেছিলাম তখন তাদের শেল স্তরে উপস্থিত ছিল না। আসুন একটি দ্রুত চেক করা যাক। আমি ম্যাকওএস এক্স-তে এমকেটেম্প খুঁজে পাই; আমার সোলারিসে এমকেটেম্প রয়েছে তবে কেবলমাত্র আমি জিএনইউ সরঞ্জাম ইনস্টল করেছি; দেখে মনে হচ্ছে এমকেটেম্প এন্টিক এইচপি-ইউএক্স-এ উপস্থিত রয়েছে। আমি নিশ্চিত নই যে এমকেটেম্পের একটি সাধারণ অনুরোধ আছে যা প্ল্যাটফর্মগুলি জুড়ে কাজ করে। পসিক্স এমকেটেম্প বা টেম্পফাইলকে মানক করে না। আমার যে প্ল্যাটফর্মগুলির অ্যাক্সেস রয়েছে তাতে আমি টেম্পফিল পাইনি। ফলস্বরূপ, আমি পোর্টেবল শেল স্ক্রিপ্টগুলিতে কমান্ডগুলি ব্যবহার করতে সক্ষম হব না।
জোনাথন লেফলার 21

4
ব্যবহার করার trapসময় মনে রাখবেন যে ব্যবহারকারী সর্বদা SIGKILLএকটি প্রক্রিয়াতে অবিলম্বে এটি বন্ধ করতে প্রেরণ করতে পারে, এক্ষেত্রে আপনার ফাঁদ কার্যকর হবে না। যখন আপনার সিস্টেমটি বিদ্যুৎ বিভ্রাটের অভিজ্ঞতা অর্জন করে তখন একই কাজ। অস্থায়ী ফাইলগুলি তৈরি করার সময়, অবশ্যই ব্যবহারের বিষয়টি নিশ্চিত করুন mktempকারণ এটি আপনার ফাইলগুলি কোথাও রাখবে, যেখানে সেগুলি পুনরায় বুট করার পরে পরিষ্কার করা হয় (সাধারণত এতে /tmp)।
জোশ

160

ইন ব্যাশ আপনি ব্যবহার করতে পারেন set -eএবং set -o pipefailআপনার ফাইলের শুরুতে। ./a | ./b | ./cতিনটি স্ক্রিপ্টের কোনওটি ব্যর্থ হলে পরবর্তী কমান্ডটি ব্যর্থ হবে। রিটার্ন কোডটি হবে প্রথম ব্যর্থ স্ক্রিপ্টের রিটার্ন কোড।

নোট যে pipefailস্ট্যান্ডার্ড sh এ উপলব্ধ নয় ।


আমি পাইপফেইল সম্পর্কে জানতাম না, সত্যই কার্যকর।
ফিল জ্যাকসন

এটি ইন্টারেক্টিভ শেল নয়, এটি কোনও স্ক্রিপ্টে রাখার উদ্দেশ্য। আপনার আচরণ সেট-এ সরল করা যেতে পারে; মিথ্যা এটি শেল প্রক্রিয়াটি থেকেও বেরিয়ে আসে, যা প্রত্যাশিত আচরণ;)
মিশেল সামিয়া

12
দ্রষ্টব্য: এটি এখনও তিনটি স্ক্রিপ্ট কার্যকর করে এবং প্রথম ত্রুটিতে পাইপটি থামায় না।
জলদি

4
@ n2liquid-GuilhermeVieira বিটিডব্লিউ, "বিভিন্ন প্রকারের" দ্বারা, আমি নির্দিষ্টভাবে বলতে চাইছিলাম, একটি বা উভয়কে set(মোট 4 টি আলাদা সংস্করণের জন্য) সরিয়ে দিন এবং দেখুন কীভাবে এটি শেষের আউটপুটকে প্রভাবিত করে echo
হাইড

4
গুগল থেকে ব্যাশে কীভাবে এটি করতে হয় তা অনুসন্ধান করতে গিয়ে @ জোসচ এই পৃষ্ঠাটি পেয়েছি এবং যদিও, "এটি আমি ঠিক খুঁজছি"। আমি সন্দেহ করি এমন অনেক লোক যারা উত্তরগুলি অগ্রাহ্য করে একই ধরণের চিন্তাধারার মধ্য দিয়ে যায় এবং ট্যাগগুলির ট্যাগ এবং সংজ্ঞা পরীক্ষা করে না।
ট্রয় ড্যানিয়েল

45

আপনি ${PIPESTATUS[]}সম্পূর্ণ সঞ্চালনের পরে অ্যারেও পরীক্ষা করতে পারেন , উদাহরণস্বরূপ:

./a | ./b | ./c

তারপরে ${PIPESTATUS}পাইপের প্রতিটি কমান্ড থেকে ত্রুটি কোডগুলির একটি অ্যারে হবে, সুতরাং যদি মাঝের কমান্ডটি ব্যর্থ হয় echo ${PIPESTATUS[@]}তবে এর মতো কিছু থাকবে:

0 1 0

এবং কমান্ডের পরে এই জাতীয় কিছু চালানো:

test ${PIPESTATUS[0]} -eq 0 -a ${PIPESTATUS[1]} -eq 0 -a ${PIPESTATUS[2]} -eq 0

পাইপের সমস্ত কমান্ড সফল হয়েছে তা আপনাকে পরীক্ষা করতে দেয়।


11
এটি বাশিষ --- এটি বাশ এক্সটেনশান এবং পসিক্স স্ট্যান্ডার্ডের অংশ নয়, সুতরাং ড্যাশ এবং অ্যাশের মতো অন্যান্য শেল এটি সমর্থন করবে না। এর অর্থ আপনি যদি শুরু হয় এমন স্ক্রিপ্টগুলিতে এটি ব্যবহার করার চেষ্টা করেন তবে আপনি সমস্যার মধ্যে পড়ে যেতে পারেন #!/bin/sh, কারণ যদি shবাশ না হয় তবে এটি কাজ করবে না। ( #!/bin/bashপরিবর্তে ব্যবহারের কথা মনে করে সহজেই স্থিরযোগ্য ))
ডেভিড

4
echo ${PIPESTATUS[@]} | grep -qE '^[0 ]+$'- $PIPESTATUS[@]কেবল 0 এবং ফাঁকা স্থান থাকলে 0 টি প্রদান করে (পাইপের সমস্ত কমান্ড সফল হলে)।
ম্যাটবিয়ানকো

@ ম্যাটবিয়ানকো এটি আমার পক্ষে সেরা সমাধান। এটি && এর সাথেও কাজ করে eg উদাহরণস্বরূপ, command1 && command2 | command3যদি সেগুলির মধ্যে কোনওরও আপনার সমাধানটি ব্যর্থ হয় তবে শূন্য থেকে ফিরে আসে।
ডেভিডসি

8

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

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

res=$( (./a 2>&1 || echo "1st failed with $?" >&2) |
(./b 2>&1 || echo "2nd failed with $?" >&2) |
(./c 2>&1 || echo "3rd failed with $?" >&2) > /dev/null 2>&1)
if [ -n "$res" ]; then
    echo pipe failed
fi

কিছু ব্যর্থ হয়েছে কিনা তা সনাক্ত করতে echoকোনও কমান্ড ব্যর্থ হলে কোনও কমান্ড স্ট্যান্ডার্ড ত্রুটিতে মুদ্রণ করে। তারপরে সম্মিলিত স্ট্যান্ডার্ড ত্রুটির আউটপুট সংরক্ষণ করা হবে $resএবং পরে তদন্ত করা হবে। এই কারণেই সমস্ত প্রক্রিয়াগুলির স্ট্যান্ডার্ড ত্রুটিটি স্ট্যান্ডার্ড আউটপুটে পুনঃনির্দেশিত হয়। আপনি সেই আউটপুটটি প্রেরণ করতে /dev/nullবা এটিকে অন্য কোনও সূচক হিসাবে রেখে দিতে পারেন যে কিছু ভুল হয়েছে। আপনি সর্বশেষ পুনর্নির্দেশটিকে /dev/nullকোনও ফাইলের সাথে প্রতিস্থাপন করতে পারেন যদি আপনি সর্বশেষ কমান্ডের আউটপুটটি কোথাও সংরক্ষণ করতে পারেন।

এই কনস্ট্রাক্ট সঙ্গে আরো বাজাতে এবং নিজেকে সন্তুষ্ট করার এই সত্যিই কি এটি করা উচিত, আমি প্রতিস্থাপিত করে যে ./a, ./bএবং ./csubshells যা চালানো দ্বারা echo, catএবং exit। আপনি এটি ব্যবহার করে এটি পরীক্ষা করে দেখতে পারেন যে এই নির্মাণটি সত্যই সমস্ত আউটপুটকে একটি প্রক্রিয়া থেকে অন্য প্রক্রিয়াতে ফরোয়ার্ড করে এবং ত্রুটি কোডগুলি সঠিকভাবে রেকর্ড হয়ে যায়।

res=$( (sh -c "echo 1st out; exit 0" 2>&1 || echo "1st failed with $?" >&2) |
(sh -c "cat; echo 2nd out; exit 0" 2>&1 || echo "2nd failed with $?" >&2) |
(sh -c "echo start; cat; echo end; exit 0" 2>&1 || echo "3rd failed with $?" >&2) > /dev/null 2>&1)
if [ -n "$res" ]; then
    echo pipe failed
fi
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.