"-O এররট্রেস" (যেমন সেট-ই) ব্যবহার করে কমান্ড প্রতিস্থাপনে ত্রুটিগুলি আটকে রাখা


14

এই রেফ ম্যানুয়াল অনুসারে :

-E (এছাড়াও -ও ভুল)

যদি সেট করা থাকে তবে ERR- এ থাকা কোনও ফাঁদ শেল ফাংশন, কমান্ড বিকল্পগুলি এবং সাব-শেল পরিবেশে সম্পাদিত কমান্ড দ্বারা উত্তরাধিকার সূত্রে প্রাপ্ত। ইআরআর ফাঁদ এ জাতীয় ক্ষেত্রে সাধারণত উত্তরাধিকার সূত্রে হয় না।

তবে, আমি অবশ্যই এটির ভুল ব্যাখ্যা করব, কারণ নিম্নলিখিতগুলি কার্যকর হয় না:

#!/usr/bin/env bash
# -*- bash -*-

set -e -o pipefail -o errtrace -o functrace

function boom {
  echo "err status: $?"
  exit $?
}
trap boom ERR


echo $( made up name )
echo "  ! should not be reached ! "

আমি ইতিমধ্যে সরল অ্যাসাইনমেন্টটি জানি, my_var=$(made_up_name)স্ক্রিপ্ট থেকে বেরিয়ে set -eআসব (অর্থাত্‍ errexit)।

হয় -E/-o errtraceউপরের কোড মত কাজ করার কথা? বা, সম্ভবত, আমি এটি ভুল পড়েছি?


2
এটা একটা ভালো প্রশ্ন। প্রতিস্থাপন করা হচ্ছে echo $( made up name )সঙ্গে $( made up name )আকাঙ্ক্ষিত আচরণ উৎপন্ন হয়। যদিও আমার কাছে এর কোন ব্যাখ্যা নেই।
ইরুবার

আমি বাশ-ই সম্পর্কে জানি না তবে আমি জানি যে - পাইপলাইনে শেষ কমান্ড থেকে ত্রুটির ফলস্বরূপ যদি কেবলমাত্র শেল প্রস্থানটি প্রভাবিত করে তবেই সে প্রভাব ফেলবে। সুতরাং আপনার var=$( pipe )এবং $( pipe )উদাহরণগুলি উভয়ই পাইপের শেষ পয়েন্টগুলি উপস্থাপন pipe > echoকরবে যেখানে এমনটি হবে না। আমার ম্যান পেজটি বলে: "১. মাল্টি-কমান্ড পাইপলাইনে কোনও পৃথক কমান্ডের ব্যর্থতা শেলটি প্রস্থান করতে পারে না Only কেবল পাইপলাইনের ব্যর্থতা বিবেচনা করা হবে
মাইকজারভ

আপনি যদিও এটি ব্যর্থ করতে পারেন: প্রতিধ্বনি $ ({{মেকআপ নাম?})। তবে সেট-ই। আবার, -আমি আমার নিজের অভিজ্ঞতার বাইরে।
মাইক্রোসার্ভ

@mikeserv @ 1_CR ব্যাশ ম্যানুয়াল @ প্রতিধ্বনি ইঙ্গিত করে যে echoসবসময় ফেরৎ 0. এই বিশ্লেষণে উপাদান করা আবশ্যক ...

উত্তর:


5

দ্রষ্টব্য: zsh"খারাপ নিদর্শনগুলি" সম্পর্কে অভিযোগ করবে যদি আপনি এখানে বেশিরভাগ উদাহরণের জন্য "ইনলাইন মন্তব্যগুলি" স্বীকার করার জন্য কনফিগার না করেন এবং আমি যেমন করেছি তেমন কোনও প্রক্সি শেলের মাধ্যমে সেগুলি চালাবেন না sh <<-\CMD

ঠিক আছে, সুতরাং আমি উপরের মন্তব্যে যেমন বলেছি, বাশেরset -E সম্পর্কে আমি বিশেষভাবে জানি না , তবে আমি জানি যে পসিএক্স সামঞ্জস্যপূর্ণ শাঁসগুলি যদি আপনি এটি চান তবে মান পরীক্ষা করার একটি সহজ উপায় সরবরাহ করে:

    sh -evx <<-\CMD
    _test() { echo $( ${empty:?error string} ) &&\
        echo "echo still works" 
    }
    _test && echo "_test doesnt fail"
    # END
    CMD
sh: line 1: empty: error string
+ echo

+ echo 'echo still works'
echo still works
+ echo '_test doesnt fail'
_test doesnt fail

সর্বোপরি আপনি দেখতে পাবেন যে, যদিও আমি ব্যবহার parameter expansionপরীক্ষা করার জন্য ${empty?} _test()এখনও returnগুলি একটি পাস - যেমন সুবিদিত হয় গত echoএই ঘটনাটি ঘটে ব্যর্থ মান নিহত $( command substitution )subshell যে এটা রয়েছে, কিন্তু তার পিতা বা মাতা শেল - _testএই সময়ে - ট্রাকিং উপর রাখে। আর echoগ্রাহ্য না করে - শুধুমাত্র সেবা করার জন্য এটা প্রচুর খুশি একটি \newline; echoহল না একটি পরীক্ষা।

তবে এটি বিবেচনা করুন:

    sh -evx <<-\CMD
    _test() { echo $( ${empty:?error string} ) &&\
            echo "echo still works" ; } 2<<-INIT
            ${empty?function doesnt run}
    INIT
    _test ||\
            echo "this doesnt even print"
    # END
    CMD
_test+ sh: line 1: empty: function doesnt run

কারণ আমি এখন _test()'sএকটি প্রাক-মূল্যায়িত প্যারামিটার দিয়ে ইনপুট খাওয়ালাম ফাংশনটি মোটেও চালানোর চেষ্টা করে না। শেল এর চেয়ে বেশি কী স্পষ্টতই পুরোপুরি ভূত ছেড়ে দেয় এবং এমনকি মুদ্রণও করে না।INIT here-document_test()shecho "this doesnt even print"

সম্ভবত যে না কি আপনি চান।

এটি ঘটায় কারণ ${var?}স্টাইলের প্যারামিটার-প্রসারণটি অনুপস্থিত প্যারামিটারের ক্ষেত্রে প্রস্থানটি ছাড়ারshell জন্য ডিজাইন করা হয়েছে , এটি এর মতো কাজ করে :

${parameter:?[word]}

ত্রুটি ইঙ্গিত করুন Nullবা Unset.যদি প্যারামিটারটি সেট না করা বা শূন্য করা হয়, expansion of word(বা কোনও শব্দ বাদ দিলে সেটটি সেট না করা একটি বার্তা) হবে written to standard errorএবং হবে shell exits with a non-zero exit statusঅন্যথায়, মান parameter shall be substitutedএকটি ইন্টারেক্টিভ শেলটি প্রস্থান করার দরকার নেই।

আমি পুরো দস্তাবেজটি অনুলিপি / পেস্ট করব না, তবে আপনি যদি কোনও set but nullমানটির জন্য ব্যর্থতা চান তবে আপনি ফর্মটি ব্যবহার করুন:

${var : error message }

সঙ্গে :colonউপরে হিসাবে। আপনি যদি nullসফল হতে একটি মান চান , তবে কোলন বাদ দিন। আপনি এটিকে অবহেলা করতে পারেন এবং কেবল সেট মানগুলির জন্য ব্যর্থ হতে পারেন, যেমন আমি এক মুহুর্তে দেখাব।

আর একটি রান _test():

    sh <<-\CMD
    _test() { echo $( ${empty:?error string} ) &&\
            echo "echo still works" ; } 2<<-INIT
            ${empty?function doesnt run}
    INIT
    echo "this runs" |\
        ( _test ; echo "this doesnt" ) ||\
            echo "now it prints"
    # END
    CMD
this runs
sh: line 1: empty: function doesnt run
now it prints

এটি সকল ধরণের দ্রুত পরীক্ষার সাথে কাজ করে, তবে উপরে আপনি এটি দেখতে পাবেন _test(), pipelineব্যর্থতার মাঝামাঝি থেকে চালানো হবে এবং command listকার্যত এটির মধ্যে থাকা কমান্ডগুলির মধ্যে কোনওটিই চালানো বা নিচে চালানো কোনওরূপ না হওয়ায় এটিতে থাকা সাবশেল পুরোপুরি ব্যর্থ হয় echo, যদিও এটি প্রদর্শিত হয় যে এটি সহজেই পরীক্ষা করা যেতে পারে কারণ echo "now it prints" এখন প্রিন্ট করে।

শয়তান বিবরণে আছে, আমার ধারণা। উপরে ক্ষেত্রে, শেল যে প্রস্থানের হয় না স্ক্রিপ্টের _main | logic | pipelineকিন্তু ( subshell in which we ${test?} ) ||একটু স্যান্ডবক্সিং জন্য বলা হয় তাই।

এবং এটি সুস্পষ্ট নাও হতে পারে তবে আপনি যদি কেবল বিপরীত ক্ষেত্রে বা কেবল set=মূল্যবোধের জন্যই পাস করতে চান তবে এটি মোটামুটি সহজ:

    sh <<-\CMD
    N= #N is NULL
    _test=$N #_test is also NULL and
    v="something you would rather do without"    
    ( #this subshell dies
        echo "v is ${v+set}: and its value is ${v:+not NULL}"
        echo "So this ${_test:-"\$_test:="} will equal ${_test:="$v"}"
        ${_test:+${N:?so you test for it with a little nesting}}
        echo "sure wish we could do some other things"
    )
    ( #this subshell does some other things 
        unset v #to ensure it is definitely unset
        echo "But here v is ${v-unset}: ${v:+you certainly wont see this}"
        echo "So this ${_test:-"\$_test:="} will equal NULL ${_test:="$v"}"
        ${_test:+${N:?is never substituted}}
        echo "so now we can do some other things" 
    )
    #and even though we set _test and unset v in the subshell
    echo "_test is still ${_test:-"NULL"} and ${v:+"v is still $v"}"
    # END
    CMD
v is set: and its value is not NULL
So this $_test:= will equal something you would rather do without
sh: line 7: N: so you test for it with a little nesting
But here v is unset:
So this $_test:= will equal NULL
so now we can do some other things
_test is still NULL and v is still something you would rather do without

উপরের উদাহরণটি POSIX পরামিতি প্রতিস্থাপনের 4 টি রূপ এবং তাদের বিভিন্ন :colon nullবা not nullপরীক্ষার সুবিধা গ্রহণ করে। উপরের লিঙ্কটিতে আরও তথ্য রয়েছে এবং এটি আবার এখানে রয়েছে

এবং আমি অনুমান করি আমাদেরও আমাদের _testফাংশনটির কাজটি দেখাতে হবে, তাই না? আমরা কেবল empty=somethingআমাদের ফাংশনটির (বা আগে যে কোনও সময়) প্যারামিটার হিসাবে ঘোষণা করি :

    sh <<-\CMD
    _test() { echo $( echo ${empty:?error string} ) &&\
            echo "echo still works" ; } 2<<-INIT
            ${empty?tested as a pass before function runs}
    INIT
    echo "this runs" >&2 |\
        ( empty=not_empty _test ; echo "yay! I print now!" ) ||\
            echo "suspiciously quiet"
    # END
    CMD
this runs
not_empty
echo still works
yay! I print now!

এটি লক্ষ করা উচিত যে এই মূল্যায়নটি একা দাঁড়িয়েছে - এটি ব্যর্থ হওয়ার জন্য কোনও অতিরিক্ত পরীক্ষা প্রয়োজন। আরও কয়েকটি উদাহরণ:

    sh <<-\CMD
    empty= 
    ${empty?null, no colon, no failure}
    unset empty
    echo "${empty?this is stderr} this is not"
    # END
    CMD
sh: line 3: empty: this is stderr

    sh <<-\CMD
    _input_fn() { set -- "$@" #redundant
            echo ${*?WHERES MY DATA?}
            #echo is not necessary though
            shift #sure hope we have more than $1 parameter
            : ${*?WHERES MY DATA?} #: do nothing, gracefully
    }
    _input_fn heres some stuff
    _input_fn one #here
    # shell dies - third try doesnt run
    _input_fn you there?
    # END
    CMD
heres some stuff
one
sh: line :5 *: WHERES MY DATA?

এবং তাই অবশেষে আমরা মূল প্রশ্নে ফিরে আসি: সাবসেলের ত্রুটিগুলি কীভাবে পরিচালনা করতে হয় $(command substitution)? সত্যটি হল - দুটি উপায় আছে তবে দুটিও সরাসরি নয়। সমস্যার মূলটি হ'ল শেলের মূল্যায়ন প্রক্রিয়া - শেলটির মূল্যায়ন প্রক্রিয়া শেলের বিস্তৃতি ( $(command substitution)বর্তমান) এর শেল কমান্ড কার্যকর করার চেয়ে শুরুর আগে ঘটে - যা তখন আপনার ত্রুটিগুলি ধরা পড়ে এবং আটকা পড়ে।

অপটির অভিজ্ঞতাগুলির বিষয়টি হ'ল বর্তমান শেল ত্রুটির জন্য মূল্যায়ন করার সময়, $(command substitution)সাব- শেলটি ইতিমধ্যে প্রতিস্থাপিত হয়ে গেছে - কোনও ত্রুটি অবশিষ্ট নেই।

তাহলে দুটি উপায় কী? হয় আপনি $(command substitution)পরীক্ষা ছাড়াই সাবস্কেলের মধ্যে স্পষ্টভাবে এটি করেন যেমন আপনি না করতেন, বা আপনি এটির ফলাফলকে একটি বর্তমান শেল ভেরিয়েবলের মধ্যে শুষে নেন এবং এর মান পরীক্ষা করেন।

পদ্ধতি 1:

    echo "$(madeup && echo \: || echo '${fail:?die}')" |\
          . /dev/stdin

sh: command not found: madeup
/dev/stdin:1: fail: die

    echo $?

126

পদ্ধতি 2:

    var="$(madeup)" ; echo "${var:?die} still not stderr"

sh: command not found: madeup
sh: var: die

    echo $?

1

প্রতি লাইনে ঘোষিত ভেরিয়েবলের সংখ্যা নির্বিশেষে এটি ব্যর্থ হবে:

   v1="$(madeup)" v2="$(ls)" ; echo "${v1:?}" "${v2:?}"

sh: command not found: madeup
sh: v1: parameter not set

এবং আমাদের ফেরতের মান স্থির থাকে:

    echo $?
1

এখন ফাঁদ:

    trap 'printf %s\\n trap resurrects shell!' ERR
    v1="$(madeup)" v2="$(printf %s\\n shown after trap)"
    echo "${v1:?#1 - still stderr}" "${v2:?invisible}"

sh: command not found: madeup
sh: v1: #1 - still stderr
trap
resurrects
shell!
shown
after
trap

    echo $?
0

উদাহরণস্বরূপ, কার্যত তার যথাযথ স্পেসিফিকেশন: প্রতিধ্বনি $ {v = $ (মেকআপের নাম)}; প্রতিধ্বনি "$ {v:? trap1st!! পৌঁছেছে না!"
মাইক্রজারভ

1
সংক্ষিপ্তসার হিসাবে: এই প্যারামিটার বিস্তৃতিগুলি যদি ভেরিয়েবলগুলি সেট করা থাকে তবে এটি নিয়ন্ত্রণের দুর্দান্ত উপায় echo "abc" "${v1:?}"e এবং শেলটি ফিরে আসে 1. এটি কোনও কমান্ডের সাথে বা ছাড়াই সত্য ( "${v1:?}"সরাসরি ক্লায়েন্টে)। তবে ওপি-র স্ক্রিপ্টের জন্য, ফাঁদটি ট্রিগার করার জন্য যা যা করা দরকার তা হ'ল তার পরিবর্তনশীল অ্যাসাইনমেন্টটি একটিমাত্র লাইনেই অস্তিত্বহীন কমান্ডের বিকল্প হিসাবে সম্বোধন করে। অন্যথায় প্রতিধ্বনি এর আচরণটি 0 টি সর্বদা ফিরে আসে, যদি না আপনি বর্ণিত পরীক্ষাগুলির মতো বাধা পান।

শুধু v=$( madeup )। এতে যেটি অনিরাপদ তা আমি দেখতে পাচ্ছি না। এটি কেবল একটি নিয়োগ, উদাহরণস্বরূপ ব্যক্তিটি আদেশটি ভুলভাবে লিখেছিল v="$(lss)"। এটি ত্রুটি। হ্যাঁ আপনি শেষ কমান্ডের ত্রুটি স্থিতি দিয়ে যাচাই করতে পারবেন $? - কারণ এটি লাইনের কমান্ড (কোনও আদেশের নাম ছাড়া একটি অ্যাসাইনমেন্ট) এবং অন্য কিছু নয় - প্রতিধ্বনিত হওয়ার আর্গুমেন্ট নয়। এছাড়াও, এখানে এটি 0 = হিসাবে ফাংশনটির দ্বারা আটকে গেছে, যাতে আপনি দুবার প্রতিক্রিয়া পান। অন্যথায় অবশ্যই আপনি ব্যাখ্যা হিসাবে একটি কাঠামোর মধ্যে এই সুশৃঙ্খল করার আরও ভাল উপায় আছে কিন্তু ওপিতে একটি একক লাইন ছিল: একটি প্রতিধ্বনি প্লাস তার ব্যর্থ বিকল্প প্রতিস্থাপন। প্রতিধ্বনি নিয়ে ভাবছিলেন তিনি।

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

1
ভাল পয়েন্ট - সম্ভবত আরও প্রচেষ্টা জন্য বলা হয়। আমি এটা চিন্তা।
মাইকজার্ভ

1

যদি সেট করা থাকে তবে ERR- এ থাকা কোনও ফাঁদ শেল ফাংশন, কমান্ড প্রতিস্থাপন এবং সাব-শেল পরিবেশে সম্পাদিত কমান্ডের মাধ্যমে উত্তরাধিকার সূত্রে প্রাপ্ত

আপনার স্ক্রিপ্টে এটি একটি আদেশ আদেশ ( echo $( made up name ))। বাশ কমান্ডগুলিতে হয় উভয় দ্বারা সীমিত করা হয় ; বা নতুন লাইন সহ । কমান্ডে

echo $( made up name )

$( made up name )কমান্ডের একটি অংশ হিসাবে বিবেচিত হয়। এমনকি যদি এই অংশটি ব্যর্থ হয় এবং ত্রুটি সহ ফিরে আসে তবে পুরো কমান্ডটি সফলভাবে কার্যকর করে কারণ echoএটি সম্পর্কে জানা নেই। কমান্ডটি 0 দিয়ে ফিরে আসার সাথে সাথে কোনও ট্র্যাপ শুরু হয়নি।

আপনাকে এটি দুটি কমান্ড, অ্যাসাইনমেন্ট এবং প্রতিধ্বনিতে স্থাপন করা দরকার

var=$(made_up_name)
echo $var

echo $varব্যর্থ হয় না - $varখালি পর্যন্ত প্রসারিত হয় echoবাস্তবে এটি একবার দেখার আগে।
মাইকজার্ভ

0

এটি ব্যাশে একটি বাগের কারণে। কমান্ড সাবস্টিটিউশন পদক্ষেপের সময়

echo $( made up name )

madeএকটি সাব-শেলের মধ্যে (বা খুঁজে পাওয়া যায় না) চালায় তবে সাবশেলটি এমনভাবে "অনুকূলিত" হয় যাতে এটি প্যারেন্ট শেল থেকে কিছু ফাঁদ ব্যবহার না করে। এটি সংস্করণ ৪.৪.৫ এ স্থির করা হয়েছিল :

নির্দিষ্ট পরিস্থিতিতে, একটি সাধারণ কমান্ডটি কাঁটাচামচ দূর করতে অপ্টিমাইজ করা হয়, যার ফলে এক্সট ফাঁদ কার্যকর হয় না uted

4.4.5 বা তার বেশি বাশ দিয়ে আপনার নিম্নলিখিত আউটপুটটি দেখতে হবে:

error.sh: line 13: made: command not found
err status: 127
  ! should not be reached !

ট্র্যাপ হ্যান্ডলারটি প্রত্যাশিত হিসাবে কল করা হয়েছিল, তারপরে সাবশেলটি প্রস্থান করে। ( set -eকেবলমাত্র পিতামাতাদের নয়, তবে সাব-শেলটি প্রস্থান করতে পারে, যাতে "পৌঁছানো উচিত নয়" বার্তাটি সত্যিকার অর্থে পৌঁছে দেওয়া উচিত))

পুরানো সংস্করণগুলির জন্য একটি কার্যপ্রণালী হ'ল একটি পূর্ণ, অ-অপ্টিমাইজড সাবসেল তৈরি করতে বাধ্য করা:

echo $( ( made up name ) )

পাটিগণিত সম্প্রসারণ থেকে আলাদা করার জন্য অতিরিক্ত স্থান প্রয়োজন

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