`Set -eu` ব্যবহার করার সময় EXIT এবং ERR ট্র্যাপগুলির সঠিক আচরণ`


27

আমি ইআরআর এবং এক্সিট ফাঁদগুলির সাথে set -e( errexit), set -u( nounset) ব্যবহার করার সময় কিছু অদ্ভুত আচরণ পর্যবেক্ষণ করছি । এগুলি সম্পর্কিত বলে মনে হচ্ছে, সুতরাং তাদের একটি প্রশ্নের মধ্যে রাখা যুক্তিসঙ্গত বলে মনে হয়।

1) set -uERR ফাঁদগুলি ট্রিগার করে না

  • কোড:

    #!/bin/bash
    trap 'echo "ERR (rc: $?)"' ERR
    set -u
    echo ${UNSET_VAR}
    
  • প্রত্যাশিত: ERR ট্র্যাপটি কল হয়ে যায়, আরসি! = 0 0
  • আসল: ERR ফাঁদ বলা হয় না , আরসি == 1
  • দ্রষ্টব্য: set -eফলাফল পরিবর্তন করে না

2) set -euএক্সিট ট্র্যাপে প্রস্থান কোডটি ব্যবহার করা 1 এর পরিবর্তে 0 হয়

  • কোড:

    #!/bin/bash
    trap 'echo "EXIT (rc: $?)"' EXIT
    set -eu
    echo ${UNSET_VAR}
    
  • প্রত্যাশিত: এক্সিট ট্র্যাপটি কল হয়ে যায়, আরসি == 1
  • আসল: এক্সিট ট্র্যাপ বলা হয়, আরসি == 0
  • দ্রষ্টব্য: ব্যবহারের সময় set +e, আরসি == ১। অন্য কোনও কমান্ড ত্রুটি ছুঁড়ে ফেললে এক্সিট ট্র্যাপ যথাযথ আরসি দেয়।
  • সম্পাদনা: এই বিষয়টিতে একটি আকর্ষণীয় মন্তব্য সহ একটি এসও পোস্ট রয়েছে যা এটি বাশ সংস্করণটি ব্যবহৃত হচ্ছে সম্পর্কিত হতে পারে বলে পরামর্শ দেয়। এই স্নিপেটটি বাশ ৪.৩.১১ এর সাথে পরীক্ষার ফলে আরসি = 1 আসে that's দুর্ভাগ্যক্রমে সমস্ত হোস্টে বাশকে (৩.২.৫১ থেকে) আপগ্রেড করা এই মুহুর্তে সম্ভব নয়, সুতরাং আমাদের আরও কিছু সমাধান নিয়ে আসতে হবে।

এই আচরণগুলির যে কেউ ব্যাখ্যা করতে পারে?

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


3
4 থেকে আমার মনে bashহয় স্ট্যান্ডার্ডটি ভেঙে সাব্হেলগুলিতে ফাঁদ ফেলতে শুরু করে। কোথা থেকে ফিরে এসেছিল সেই ফাঁদটি একই পরিবেশে কার্যকর হওয়ার কথা, তবে bashকিছুক্ষণের জন্য তা করেননি।
মাইক্রজারভ

1
এক মিনিট অপেক্ষা করুন - আপনি কি সমাধান চান বা ব্যাখ্যা চান? এবং যদি আপনি একটি সমাধান চান, তাহলে ঠিক কি একটি সমাধান? আপনি কি ঘটতে চান? set -eএবং set -uউভয়ই বিশেষভাবে স্ক্রিপ্টেড শেলটি মারার জন্য ডিজাইন করা হয়েছে । তাদের অ্যাপ্লিকেশন ট্রিগার করতে পারে এমন পরিস্থিতিতে তাদের ব্যবহার করা একটি স্ক্রিপ্ট শেলকে হত্যা করবে । এগুলি ব্যবহার না করা এবং পরিবর্তে যখন তারা কোনও কোড ক্রম প্রয়োগ করে তখন এই শর্তগুলির জন্য পরীক্ষা করে নেওয়া ছাড়া এটির কোনও লাভ নেই । সুতরাং, মূলত, আপনি ভাল শেল-কোড লিখতে পারেন, বা আপনি ব্যবহার করতে পারেন set -eu
মাইকসার্ভ

2
প্রকৃতপক্ষে, আমি উভয়ের সন্ধান করছি, কেননা -uইআরআর ফাঁদটি কেন ট্রিগার করবে না সে সম্পর্কে পর্যাপ্ত তথ্য আমি পাইনি (এটি একটি ত্রুটি, সুতরাং এটি ফাঁদটি ট্রিগার করা উচিত নয়) বা ত্রুটি কোডটি 1 এর পরিবর্তে 0 হয় The পরের সংস্করণে ইতিমধ্যে ঠিক করা হয়েছে এমন একটি বাগ বলে মনে হচ্ছে, তাই। তবে প্রথম অংশটি বোঝা বেশ কঠিন যদি আপনি বুঝতে না পারেন যে শেল মূল্যায়নের ত্রুটিগুলি (প্যারামিটার সম্প্রসারণ) এবং কমান্ডগুলিতে প্রকৃত ত্রুটি দুটি পৃথক জিনিস বলে মনে হচ্ছে। সমাধানের জন্য, ভাল, আপনি যেমন পরামর্শ দিয়েছিলেন, আমি এখন এড়াতে চেষ্টা করছি -euএবং এটির প্রয়োজন হলে ম্যানুয়ালি পরীক্ষা করতে চাই।
ডিভিডিজেং

1
@ ডিভিডিএসং - ভাল এটি যাওয়ার উপায় - আপনি যখন উত্তর হিসাবে করবেন তখন আপনার স্ক্রিপ্টটি পোস্ট করা উচিত এবং নিজেকে অনুগ্রহ প্রদান করুন। আমি এই বিকল্পগুলি সত্যিই অপছন্দ করি - তারা কোনও নিরাপদ উপায়ে ব্যতিক্রম পরিচালনার অনুমতি দেয় না।
মাইকজারভেজ

1
@ ডিভিডিএসএনজি - যেখানে এই বিকল্পগুলির কোনওটি কার্যকর হতে পারে, যদিও এটি একটি সাব-সেলড প্রসঙ্গে। এবং সুতরাং এটি অনুমেয় যে আপনি এর আগে যা কিছু ব্যবহার করেছিলেন সেগুলি স্থানীয়ভাবে যেমন: (set -u; : $UNSET_VAR)এবং অনুরূপ প্রসঙ্গে রূপান্তরিত করা যেতে পারে । এই ধরণের জিনিসগুলি খুব ভাল হতে পারে - আপনি &&মাঝেমধ্যে প্রচুর পরিমাণে ফেলে দিতে পারেন:, (set -e; mkdir dir; cd dir; touch dirfile)আপনি যদি আমার ড্রিফ্ট পান। এটি ঠিক যেগুলি নিয়ন্ত্রিত প্রসঙ্গগুলি - আপনি এগুলি বিশ্বব্যাপী বিকল্প হিসাবে সেট করার পরে আপনি নিয়ন্ত্রণ হারাবেন এবং নিয়ন্ত্রিত হয়ে উঠবেন। যদিও সাধারণত আরও কার্যকর সমাধান থাকে।
মাইকসার্ভ

উত্তর:


15

থেকে man bash:

  • set -u
    • বিশেষ পরামিতিগুলি ব্যতীত আনসেট পরিবর্তনশীল এবং পরামিতিগুলি "@"এবং "*"প্যারামিটার সম্প্রসারণের সময় ত্রুটি হিসাবে আচরণ করুন Treat যদি আনসেট ভেরিয়েবল বা প্যারামিটারে প্রসারণের চেষ্টা করা হয়, শেলটি একটি ত্রুটি বার্তা প্রিন্ট করে, এবং যদি না -iপার্থক্যমূলক হয় তবে একটি ননজারো স্থিতি সহ প্রস্থান করে।

পসিএক্স জানিয়েছে যে কোনও প্রসারণ ত্রুটির ক্ষেত্রে যখন একটি প্রসারিত শেল বিশেষ অন্তর্নির্মিত (যা একটি পার্থক্য নিয়মিতভাবে উপেক্ষা করা হয়, এবং সম্ভবত অপ্রাসঙ্গিক হয়) বা এর সাথে অন্য কোনও ইউটিলিটি যুক্ত হওয়ার সাথে সাথে একটি অ-ইন্টারেক্টিভ শেলটি প্রস্থান করবে besides ।bash

  • শেল ত্রুটির ফলাফল :
    • একটি সম্প্রসারণ ত্রুটি এক যখন শেল প্রসারণও সংজ্ঞায়িত ঘটে যে ওয়ার্ড প্রসারণ আউট বাহিত হয় (যেমন, "${x!y}"কারণ !একটি বৈধ অপারেটর নয়) ; কোনও বাস্তবায়ন এটিকে সিনট্যাক্স ত্রুটি হিসাবে বিবেচনা করতে পারে যদি এটি প্রসারণের পরিবর্তে টোকেনাইজেশনের সময় সনাক্ত করতে সক্ষম হয়।
    • [এ] এন ইন্টারেক্টিভ শেলটি প্রস্থান না করে স্ট্যান্ডার্ড ত্রুটিতে ডায়াগনস্টিক বার্তা লিখবে।

এছাড়াও থেকে man bash:

  • trap ... ERR
    • একটি sigspec হলে ছিল ERR , কমান্ড ARG মৃত্যুদন্ড কার্যকর যখনই একটি পাইপলাইন হয় (যা একটি একক সহজ কমান্ড গঠিত হতে পারে) , একটি তালিকা, বা একটি যৌগ কমান্ড নিম্নবর্ণিত শর্ত একটি নন-জিরো প্রস্থান অবস্থা, বিষয় ফেরৎ:
      • ছিল ERR ফাঁদ মৃত্যুদন্ড কার্যকর না হয় ব্যর্থ কমান্ড কমান্ড তালিকা অংশ অবিলম্বে অনুসরণ করছে whileবা untilশব্দ ...
      • ... একটি ifবিবৃতিতে পরীক্ষার অংশ ...
      • ... চূড়ান্ত অনুসরণ করা কমান্ড ব্যতীত একটি &&বা ||তালিকায় মৃত্যুদন্ড কার্যকর করা একটি কমান্ডের অংশ &&বা ||...
      • ... পাইপলাইনে কোনও কমান্ড কিন্তু শেষ ...
      • ... অথবা কমান্ডের রিটার্ন মান ব্যবহার করে উল্টানো হচ্ছে !
    • এই একই শর্তাবলী এররেক্সিট -e বিকল্প দ্বারা মান্য করা হয় ।

উপরে নোট করুন যে ERR ট্র্যাপটি অন্য কোনও কমান্ডের ফেরতের মূল্যায়ন সম্পর্কে about কিন্তু যখন কোনও প্রসারণ ত্রুটি ঘটে তখন কোনও কিছুই ফেরত দেওয়ার জন্য কোনও কমান্ড চালানো হয় না। আপনার উদাহরণে, echo কখনই ঘটে না - কারণ শেলটি তার যুক্তিগুলির মূল্যায়ন ও প্রসারণ করার সময় এটি একটি -uএনসেট ভেরিয়েবলের মুখোমুখি হয়, যা বর্তমান, স্ক্রিপ্টযুক্ত শেল থেকে তাত্ক্ষণিক প্রস্থান করার জন্য স্পষ্ট শেল বিকল্প দ্বারা নির্দিষ্ট করা হয়েছে।

এবং সুতরাং এক্সিট ট্র্যাপটি , যদি কোনও হয় তবে কার্যকর হয় এবং শেলটি ডায়াগনস্টিক বার্তা এবং 0 ব্যতীত প্রস্থান স্থিতি দিয়ে বের হয় - ঠিক যেমনটি করা উচিত।

হিসাবে রেসিন: 0 জিনিস, আমি আশা যে কোন ধরণের একটি সংস্করণ নির্দিষ্ট বাগ সংশোধন করা হয় - সম্ভবত দুটি ট্রিগার সঙ্গে কাজ করতে প্রস্থান করুন একই সময়ে ঘটছে এবং এক এর অন্যান্য প্রস্থান কোড পেতে (যা ঘটতে উচিত নয়) । এবং যাইহোক, bashইনস্টল করা হিসাবে একটি আপ টু ডেট বাইনারি সহ pacman:

bash <<\IN
    printf "shell options:\t$-\n"
    trap 'echo "EXIT (rc: $?)"' EXIT
    set -eu
    echo ${UNSET_VAR}
IN

আমি প্রথম লাইনটি যুক্ত করেছি যাতে আপনি দেখতে পান যে শেলের শর্তগুলি স্ক্রিপ্টেড শেলের মতো - এটি ইন্টারেক্টিভ নয় । আউটপুটটি হ'ল:

shell options:  hB
bash: line 4: UNSET_VAR: unbound variable
EXIT (rc: 1)

সাম্প্রতিক পরিবর্তনগুলি থেকে কিছু সম্পর্কিত নোট এখানে দেওয়া হয়েছে :

  • একটি ত্রুটি সংশোধন করা হয়েছে যার কারণে অ্যাসিক্রোনাস কমান্ডগুলি $?সঠিকভাবে সেট না করা হয়েছে।
  • একটি বাগের উপস্থিতির ফলে ত্রুটি দ্বারা উত্পন্ন বার্তা ফিক্সড সম্প্রসারণ ত্রুটি মধ্যে forভুল লাইন নম্বর আছে আদেশগুলি।
  • একটি ত্রুটি সংশোধন করা হয়েছে যার ফলে সিগিন্ট এবং সিকুইটিtrap অ্যাসিঙ্ক্রোনাস সাবসেল কমান্ডগুলিতে প্যাবল হতে পারে না ।
  • বাধা হ্যান্ডলিংয়ের সাথে এমন একটি সমস্যা সমাধান করা হয়েছে যার কারণে দ্বিতীয় এবং তার পরবর্তী সংকেত ইন্টারেক্টিভ শাঁস দ্বারা উপেক্ষা করা হয়েছিল।
  • শেলটি trapসেই সংকেতগুলির জন্য হ্যান্ডলারগুলি চালনার সময় সংকেত প্রাপ্তিটিকে আর আটকাবে না এবং বেশিরভাগ trap হ্যান্ডলারের পুনরাবৃত্তভাবে চালানোর অনুমতি দেয় ( trapএকটি trapহ্যান্ডলার যখন সঞ্চালিত হয় তখন চলমান হ্যান্ডলারগুলি )

আমি মনে করি এটি হয় সর্বশেষ বা প্রথমটি সবচেয়ে প্রাসঙ্গিক - অথবা সম্ভবত দুটির সংমিশ্রণ ঘটে। একজন trapহ্যান্ডলার হ'ল তার প্রকৃতিগতভাবে অ্যাসিনক্রোনাস কারণ এটির পুরো কাজ হ'ল অ্যাসিনক্রোনাস সিগন্যালের জন্য অপেক্ষা করা এবং পরিচালনা করা । এবং আপনি -euএবং একই সাথে দুটি ট্রিগার $UNSET_VAR

এবং তাই সম্ভবত আপনার কেবল আপডেট করা উচিত, তবে আপনি যদি নিজের পছন্দ করেন তবে আপনি এটি পুরোপুরি একটি আলাদা শেল দিয়ে করব।


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

আমি আপনাকে অনুগ্রহ দিচ্ছি কারণ আপনার ব্যাখ্যাটি সবচেয়ে সহায়ক ছিল।
ডিভিডিজেং

@ ডিভিডিজেং - গ্রেসিয়াস কৌতূহল থেকে, আপনি কি কখনও ডাব্লু / আপনার সমাধান এসেছিলেন?
মাইকজার্ভ

9

(আমি ব্যাশ ৪.২.৫৩ ব্যবহার করছি)। অংশ 1 এর জন্য, ব্যাশ ম্যান পৃষ্ঠাটি কেবলমাত্র "ত্রুটি বার্তাটি স্ট্যান্ডার্ড ত্রুটিতে লেখা হবে এবং একটি ইন্টারেক্টিভ শেল প্রস্থান করবে" says এটি বলছে না যে কোনও ইআরআর ট্র্যাপ কল করা হবে, যদিও আমি সম্মত হই যে এটি যদি কার্যকর হয় তবে।

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

#!/bin/bash
trap 'rc=$?; echo "ERR at line ${LINENO} (rc: $rc)"; exit $rc' ERR
trap 'rc=$?; echo "EXIT (rc: $rc)"; exit $rc' EXIT
set -u
set -E # export trap to functions

cmd(){
 echo "args=$*"
 echo ${UNSET_VAR}
 echo hello
}
oops(){
 rc=$?
 echo "$@"
 return $rc # provoke ERR trap
}

exec 3>&1 # copy stdin to use in $()
if output=$(cmd "$@" 2>&1 >&3) # collect stderr, not stdout 
then    echo ok
else    oops "fail: $output"
fi

আমার বাশ আমি পেতে

./script my stuff; echo "exit was $?"
args=my stuff
fail: ./script: line 9: UNSET_VAR: unbound variable
ERR at line 15 (rc: 1)
EXIT (rc: 1)
exit was 1

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