বাশে হ্যান্ডল করার সময় ত্রুটি


239

বাশে ত্রুটিগুলি পরিচালনা করার জন্য আপনার প্রিয় পদ্ধতিটি কী? ওয়েবে আমি যে ত্রুটিগুলি পেয়েছি তার সর্বোত্তম উদাহরণ হ'ল উইলিয়াম শটস, জুনিয়র লিখেছিলেন http://www.linuxcommand.org এ

বাশে ত্রুটি পরিচালনা করার জন্য নিম্নলিখিত ফাংশনটি ব্যবহার করার পরামর্শ দিয়েছেন:

#!/bin/bash

# A slicker error handling routine

# I put a variable in my scripts named PROGNAME which
# holds the name of the program being run.  You can get this
# value from the first item on the command line ($0).

# Reference: This was copied from <http://www.linuxcommand.org/wss0150.php>

PROGNAME=$(basename $0)

function error_exit
{

#   ----------------------------------------------------------------
#   Function for exit due to fatal program error
#       Accepts 1 argument:
#           string containing descriptive error message
#   ---------------------------------------------------------------- 

    echo "${PROGNAME}: ${1:-"Unknown Error"}" 1>&2
    exit 1
}

# Example call of the error_exit function.  Note the inclusion
# of the LINENO environment variable.  It contains the current
# line number.

echo "Example of error with line number and message"
error_exit "$LINENO: An error has occurred."

বাশ স্ক্রিপ্টগুলিতে আপনি যে নিয়মিত ব্যবহার করছেন তার নিয়মিত পরিচালনা করার কি আরও ভাল?



1
লগিং এবং ত্রুটি পরিচালনা পরিচালনা এখানে বাস্তবায়ন দেখুন: github.com/codefirester/base/blob/master/lib/stdlib.sh
কোডফোরস্টার

উত্তর:


154

একটি ফাঁদ ব্যবহার করুন!

tempfiles=( )
cleanup() {
  rm -f "${tempfiles[@]}"
}
trap cleanup 0

error() {
  local parent_lineno="$1"
  local message="$2"
  local code="${3:-1}"
  if [[ -n "$message" ]] ; then
    echo "Error on or near line ${parent_lineno}: ${message}; exiting with status ${code}"
  else
    echo "Error on or near line ${parent_lineno}; exiting with status ${code}"
  fi
  exit "${code}"
}
trap 'error ${LINENO}' ERR

... তারপরে, যখনই আপনি একটি অস্থায়ী ফাইল তৈরি করেন:

temp_foo="$(mktemp -t foobar.XXXXXX)"
tempfiles+=( "$temp_foo" )

এবং $temp_fooপ্রস্থান করার সময় মুছে ফেলা হবে, এবং বর্তমান লাইন নম্বর মুদ্রিত হবে। ( set -eএকইভাবে আপনাকে প্রস্থান-অন-ত্রুটি আচরণ দেবে, যদিও এটি গুরুতর সতর্কতা সহ আসে এবং কোডের পূর্বাভাস এবং বহনযোগ্যতা দুর্বল করে)।

আপনি হয় ফাঁদটি আপনার errorজন্য কল করতে পারেন (এই ক্ষেত্রে এটি 1 এর কোনও ডিফল্ট প্রস্থান কোড এবং কোনও বার্তা ব্যবহার করে না) বা নিজেকে কল করে স্পষ্ট মান সরবরাহ করতে পারে; এই ক্ষেত্রে:

error ${LINENO} "the foobar failed" 2

স্থিতি 2 সহ প্রস্থান করবে এবং একটি স্পষ্ট বার্তা দেবে।


4
@ প্রশাসনের পরিবর্তনশীল মূলধনটি ইচ্ছাকৃত। সমস্ত ক্যাপগুলি কেবল শেল বিল্টিন এবং পরিবেশের ভেরিয়েবলগুলির জন্য প্রচলিত - অন্য যে কোনও কিছুর জন্য ছোট হাতের অক্ষর ব্যবহার করে নেমস্পেস বিরোধগুলি প্রতিরোধ করে। এছাড়াও দেখুন স্ট্যাকওভারফ্লো.com
চার্লস ডাফি

1
আপনি এটি আবার ভাঙ্গার আগে, আপনার পরিবর্তন পরীক্ষা করুন। কনভেনশনগুলি একটি ভাল জিনিস, তবে সেগুলি কার্য সম্পাদনের কোডের ক্ষেত্রে গৌণ।
ড্রিমন

3
@ ড্রেমন, আমি আসলে একমত নই স্পষ্টতই ভাঙা কোডটি লক্ষ্য করা এবং ঠিক হয়ে যায়। খারাপ অভ্যাস কিন্তু বেশিরভাগ-কার্যকারী কোড চিরকাল বেঁচে থাকে (এবং প্রচারিত হয়)।
চার্লস ডাফি

1
কিন্তু আপনি খেয়াল করেন নি। ব্রোকন কোডটি লক্ষ্য করা যায় কারণ কার্যকরী কোডটি প্রাথমিক উদ্বেগ।
ড্রিমন

5
এটি একেবারেই কৃত্রিম নয় ( স্ট্যাকওভারফ্লো.com / a / 10927223 / 26334 ) এবং কোডটি ইতিমধ্যে পসিক্সের সাথে ফাংশন কীওয়ার্ড অপসারণের সাথে বেমানান থাকলে পসিক্স শ এর অধীনে চালানো আর সক্ষম করে না তবে আমার মূল কথাটি হ'ল আপনি ' ve (আইএমও) সেট-ই ব্যবহারের প্রস্তাবনা দুর্বল করে উত্তরটি অবলম্বন করেছে। স্ট্যাকওভারফ্লো "আপনার" কোড সম্পর্কে নয়, এটির সেরা উত্তর রয়েছে।
ড্রইমন

123

এটি একটি সূক্ষ্ম সমাধান। আমি শুধু যুক্ত করতে চেয়েছিলাম

set -e

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


29
set -eগোটাচস ছাড়া নয়: বেশ কয়েকটিটির জন্য mywiki.wooledge.org/BashFAQ/105 দেখুন ।
চার্লস ডাফি

3
@ চার্লসডুফি, কিছু গ্যাটাচকে কাটিয়ে উঠতে পারেset -o pipefail
ob ই

7
@ চার্লসডুফি গ্যাটাচসকে নির্দেশ করার জন্য ধন্যবাদ; সামগ্রিকভাবে যদিও, আমি এখনও মনে করি set -eএকটি উচ্চ বেনিফিট-ব্যয় অনুপাত আছে।
ব্রুনো ডি ফ্রেইন

3
@ ব্রুনোডেফ্রেন আমি set -eনিজেই ব্যবহার করি, তবে irc.freenode.org # বাশ-এর ​​অন্যান্য নিয়মিত বেশিরভাগই এর বিরুদ্ধে পরামর্শ দেয় (বেশ শক্তিশালী ভাষায়)। সর্বনিম্ন, প্রশ্নে থাকা গেটছগুলি ভালভাবে বোঝা উচিত।
চার্লস ডাফি

3
সেট-ই-পাইপফেইল -u # এবং জেনে নিন আপনি কী করছেন
স্যাম ওয়াটকিন্স

78

এই পৃষ্ঠায় সমস্ত উত্তর পড়া আমাকে অনেক অনুপ্রাণিত করেছিল।

সুতরাং, এখানে আমার ইঙ্গিত:

ফাইল সামগ্রী: lib.trap.sh

lib_name='trap'
lib_version=20121026

stderr_log="/dev/shm/stderr.log"

#
# TO BE SOURCED ONLY ONCE:
#
###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##

if test "${g_libs[$lib_name]+_}"; then
    return 0
else
    if test ${#g_libs[@]} == 0; then
        declare -A g_libs
    fi
    g_libs[$lib_name]=$lib_version
fi


#
# MAIN CODE:
#
###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##

set -o pipefail  # trace ERR through pipes
set -o errtrace  # trace ERR through 'time command' and other functions
set -o nounset   ## set -u : exit the script if you try to use an uninitialised variable
set -o errexit   ## set -e : exit the script if any statement returns a non-true return value

exec 2>"$stderr_log"


###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
#
# FUNCTION: EXIT_HANDLER
#
###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##

function exit_handler ()
{
    local error_code="$?"

    test $error_code == 0 && return;

    #
    # LOCAL VARIABLES:
    # ------------------------------------------------------------------
    #    
    local i=0
    local regex=''
    local mem=''

    local error_file=''
    local error_lineno=''
    local error_message='unknown'

    local lineno=''


    #
    # PRINT THE HEADER:
    # ------------------------------------------------------------------
    #
    # Color the output if it's an interactive terminal
    test -t 1 && tput bold; tput setf 4                                 ## red bold
    echo -e "\n(!) EXIT HANDLER:\n"


    #
    # GETTING LAST ERROR OCCURRED:
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #

    #
    # Read last file from the error log
    # ------------------------------------------------------------------
    #
    if test -f "$stderr_log"
        then
            stderr=$( tail -n 1 "$stderr_log" )
            rm "$stderr_log"
    fi

    #
    # Managing the line to extract information:
    # ------------------------------------------------------------------
    #

    if test -n "$stderr"
        then        
            # Exploding stderr on :
            mem="$IFS"
            local shrunk_stderr=$( echo "$stderr" | sed 's/\: /\:/g' )
            IFS=':'
            local stderr_parts=( $shrunk_stderr )
            IFS="$mem"

            # Storing information on the error
            error_file="${stderr_parts[0]}"
            error_lineno="${stderr_parts[1]}"
            error_message=""

            for (( i = 3; i <= ${#stderr_parts[@]}; i++ ))
                do
                    error_message="$error_message "${stderr_parts[$i-1]}": "
            done

            # Removing last ':' (colon character)
            error_message="${error_message%:*}"

            # Trim
            error_message="$( echo "$error_message" | sed -e 's/^[ \t]*//' | sed -e 's/[ \t]*$//' )"
    fi

    #
    # GETTING BACKTRACE:
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
    _backtrace=$( backtrace 2 )


    #
    # MANAGING THE OUTPUT:
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #

    local lineno=""
    regex='^([a-z]{1,}) ([0-9]{1,})$'

    if [[ $error_lineno =~ $regex ]]

        # The error line was found on the log
        # (e.g. type 'ff' without quotes wherever)
        # --------------------------------------------------------------
        then
            local row="${BASH_REMATCH[1]}"
            lineno="${BASH_REMATCH[2]}"

            echo -e "FILE:\t\t${error_file}"
            echo -e "${row^^}:\t\t${lineno}\n"

            echo -e "ERROR CODE:\t${error_code}"             
            test -t 1 && tput setf 6                                    ## white yellow
            echo -e "ERROR MESSAGE:\n$error_message"


        else
            regex="^${error_file}\$|^${error_file}\s+|\s+${error_file}\s+|\s+${error_file}\$"
            if [[ "$_backtrace" =~ $regex ]]

                # The file was found on the log but not the error line
                # (could not reproduce this case so far)
                # ------------------------------------------------------
                then
                    echo -e "FILE:\t\t$error_file"
                    echo -e "ROW:\t\tunknown\n"

                    echo -e "ERROR CODE:\t${error_code}"
                    test -t 1 && tput setf 6                            ## white yellow
                    echo -e "ERROR MESSAGE:\n${stderr}"

                # Neither the error line nor the error file was found on the log
                # (e.g. type 'cp ffd fdf' without quotes wherever)
                # ------------------------------------------------------
                else
                    #
                    # The error file is the first on backtrace list:

                    # Exploding backtrace on newlines
                    mem=$IFS
                    IFS='
                    '
                    #
                    # Substring: I keep only the carriage return
                    # (others needed only for tabbing purpose)
                    IFS=${IFS:0:1}
                    local lines=( $_backtrace )

                    IFS=$mem

                    error_file=""

                    if test -n "${lines[1]}"
                        then
                            array=( ${lines[1]} )

                            for (( i=2; i<${#array[@]}; i++ ))
                                do
                                    error_file="$error_file ${array[$i]}"
                            done

                            # Trim
                            error_file="$( echo "$error_file" | sed -e 's/^[ \t]*//' | sed -e 's/[ \t]*$//' )"
                    fi

                    echo -e "FILE:\t\t$error_file"
                    echo -e "ROW:\t\tunknown\n"

                    echo -e "ERROR CODE:\t${error_code}"
                    test -t 1 && tput setf 6                            ## white yellow
                    if test -n "${stderr}"
                        then
                            echo -e "ERROR MESSAGE:\n${stderr}"
                        else
                            echo -e "ERROR MESSAGE:\n${error_message}"
                    fi
            fi
    fi

    #
    # PRINTING THE BACKTRACE:
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #

    test -t 1 && tput setf 7                                            ## white bold
    echo -e "\n$_backtrace\n"

    #
    # EXITING:
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #

    test -t 1 && tput setf 4                                            ## red bold
    echo "Exiting!"

    test -t 1 && tput sgr0 # Reset terminal

    exit "$error_code"
}
trap exit_handler EXIT                                                  # ! ! ! TRAP EXIT ! ! !
trap exit ERR                                                           # ! ! ! TRAP ERR ! ! !


###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
#
# FUNCTION: BACKTRACE
#
###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##

function backtrace
{
    local _start_from_=0

    local params=( "$@" )
    if (( "${#params[@]}" >= "1" ))
        then
            _start_from_="$1"
    fi

    local i=0
    local first=false
    while caller $i > /dev/null
    do
        if test -n "$_start_from_" && (( "$i" + 1   >= "$_start_from_" ))
            then
                if test "$first" == false
                    then
                        echo "BACKTRACE IS:"
                        first=true
                fi
                caller $i
        fi
        let "i=i+1"
    done
}

return 0



ব্যবহারের উদাহরণ:
ফাইল সামগ্রী: trap-test.sh

#!/bin/bash

source 'lib.trap.sh'

echo "doing something wrong now .."
echo "$foo"

exit 0


চলমান:

bash trap-test.sh

আউটপুট:

doing something wrong now ..

(!) EXIT HANDLER:

FILE:       trap-test.sh
LINE:       6

ERROR CODE: 1
ERROR MESSAGE:
foo:   unassigned variable

BACKTRACE IS:
1 main trap-test.sh

Exiting!


আপনি নীচের স্ক্রিনশট থেকে দেখতে পাচ্ছেন, আউটপুট রঙিন এবং ত্রুটি বার্তা ব্যবহৃত ভাষায় আসে।

এখানে চিত্র বর্ণনা লিখুন


3
এই জিনিসটি দুর্দান্ত ... এটির জন্য আপনার একটি গিথুব প্রকল্প তৈরি করা উচিত, যাতে লোকেরা সহজেই উন্নতি করতে পারে এবং পিছনে অবদান রাখতে পারে। আমি এটিকে log4bash এর সাথে সংযুক্ত করেছি এবং এটি একসাথে ভাল ব্যাশ স্ক্রিপ্টগুলি তৈরি করার জন্য একটি শক্তিশালী vভ তৈরি করে।
ডোমিনিক ডোর

1
এফওয়াইআই - test ${#g_libs[@]} == 0পসিএক্স-কমপ্লায়েন্ট নয় (পসিক্স পরীক্ষা =স্ট্রিং তুলনা বা -eqসংখ্যার তুলনার জন্য সমর্থন করে তবে ==পসিক্সে অ্যারের অভাবের কথা উল্লেখ না করে), এবং আপনি যদি পসিক্স অনুগত হওয়ার চেষ্টা করছেন না , তবে কেন বিশ্ব আপনি testগণিত প্রসঙ্গে বরং কিছুটা ব্যবহার করছেন? (( ${#g_libs[@]} == 0 ))সব পরে, পড়া সহজ।
চার্লস ডাফি

2
@ লুকা - এটি সত্যিই দুর্দান্ত! আপনার ছবি আমাকে এটির নিজস্ব বাস্তবায়ন তৈরি করতে অনুপ্রাণিত করেছিল, এটি এটি আরও কয়েক ধাপ এগিয়ে নিয়ে যায়। আমি আমার উত্তরে এটি নীচে পোস্ট করেছি ।
নিনিয়ান

3
Bravissimo !! এটি কোনও স্ক্রিপ্ট ডিবাগ করার একটি দুর্দান্ত উপায়। গ্রাজি মিল কেবলমাত্র আমি যুক্ত করেছিলাম ওএস এক্সের জন্য এটি চেক করা ছিল: case "$(uname)" in Darwin ) stderr_log="${TMPDIR}stderr.log";; Linux ) stderr_log="/dev/shm/stderr.log";; * ) stderr_log="/dev/shm/stderr.log" ;; esac
স্যাক্সডাডি

1
কিছুটা নির্লজ্জ স্ব-প্লাগ, তবে আমরা এই স্নিপেটটি নিয়েছি, এটি পরিষ্কার করেছি, আরও বৈশিষ্ট্য যুক্ত করেছি, আউটপুট বিন্যাসকে উন্নত করেছি এবং আরও পসিক্সকে সামঞ্জস্য করেছি (লিনাক্স এবং ওএসএক্স উভয় ক্ষেত্রেই কাজ করে)। এটি গিথুবে প্রাইভেক্স শেলকোর অংশ হিসাবে প্রকাশিত হয়েছে: github.com/Privex/shell-core
Someguy123

22

"সেট-ই" এর সমতুল্য বিকল্প

set -o errexit

এটি "-e" এর চেয়ে পতাকাটির অর্থ কিছুটা স্পষ্ট করে তোলে।

এলোমেলো সংযোজন: সাময়িকভাবে পতাকাটি অক্ষম করতে এবং ডিফল্টটিতে ফিরে যেতে (প্রস্থান কোড নির্বিশেষে চালিয়ে যাওয়া চালিয়ে যাওয়া), কেবলমাত্র ব্যবহার করুন

set +e
echo "commands run here returning non-zero exit codes will not cause the entire script to fail"
echo "false returns 1 as an exit code"
false
set -e

এটি অন্যান্য প্রতিক্রিয়াগুলিতে উল্লিখিত যথাযথ ত্রুটি পরিচালনা পরিচালনাকে অন্তর্ভুক্ত করে, তবে দ্রুত এবং কার্যকর (ঠিক বাশের মতো)।


1
$(foo)খালি লাইনের পরিবর্তে ব্যবহার করা fooসাধারণত ভুল জিনিস। এটি উদাহরণ হিসাবে দিয়ে প্রচার কেন?
চার্লস ডাফি

20

এখানে উপস্থাপিত ধারণাগুলি দ্বারা অনুপ্রাণিত হয়ে আমি আমার ব্যাশ বয়লারপ্লেট প্রকল্পে ব্যাশ স্ক্রিপ্টগুলিতে ত্রুটিগুলি পরিচালনা করার জন্য একটি পঠনযোগ্য এবং সুবিধাজনক উপায় তৈরি করেছি ।

কেবল গ্রন্থাগারটি সোর্স করার মাধ্যমে আপনি নিম্নলিখিতটি বাক্সের বাইরে পেয়ে যাবেন (অর্থাত্ এটি কোনও ত্রুটির কারণে কার্যকর হওয়া বন্ধ করবে, set -eযেন একটি trapঅন ERRএবং কিছু বাশ-ফুকে ধন্যবাদ ব্যবহার করে ):

বাশ-ও-ফ্রেমওয়ার্ক ত্রুটি হ্যান্ডলিং

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

অবক্ষয়টি হ'ল - এটি পোর্টেবল নয় - কোডটি ব্যাশে কাজ করে, সম্ভবত> = 4 কেবল (তবে আমি কল্পনা করতে পারি যে এটি 3 বাশ করার কিছু চেষ্টা করে পোর্ট করা যেতে পারে)।

আরও ভাল পরিচালনা করার জন্য কোডটি একাধিক ফাইলে বিভক্ত করা হয়েছে তবে আমি ব্যাকট্রেস ধারণা থেকে অনুপ্রাণিত হয়েছি লুকা বোররিওনের উপরের উত্তর হয়েছি

আরও পড়তে বা উত্সটি একবার দেখতে, গিটহাব দেখুন:

https://github.com/niieani/bash-oo-framework#error-handling-with-exceptions-and-throw


এটি বাশ অবজেক্ট ওরিয়েন্টেড ফ্রেমওয়ার্ক প্রকল্পের মধ্যে। ... ভাগ্যক্রমে এটিতে কেবল 7.4k এলওসি রয়েছে ( জিওএলপি অনুসারে )। ওওপি - অবজেক্ট-ভিত্তিক ব্যথা?
ingyhere

@ যেহেতু এটি অত্যন্ত মডুলার (এবং মুছুন-বন্ধুত্বপূর্ণ), তাই আপনি কেবল ব্যতিক্রম অংশটি ব্যবহার করতে পারেন যদি আপনি এটির জন্য এসেছিলেন;)
এটির

11

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

#This function is used to cleanly exit any script. It does this displaying a
# given error message, and exiting with an error code.
function error_exit {
    echo
    echo "$@"
    exit 1
}
#Trap the killer signals so that we can exit with a good message.
trap "error_exit 'Received signal SIGHUP'" SIGHUP
trap "error_exit 'Received signal SIGINT'" SIGINT
trap "error_exit 'Received signal SIGTERM'" SIGTERM

#Alias the function so that it will print a message with the following format:
#prog-name(@line#): message
#We have to explicitly allow aliases, we do this because they make calling the
#function much easier (see example).
shopt -s expand_aliases
alias die='error_exit "Error ${0}(@`echo $(( $LINENO - 1 ))`):"'

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

#This is an example useage, it will print out
#Error prog-name (@1): Who knew false is false.
if ! /bin/false ; then
    die "Who knew false is false."
fi

2
"আমাদের সুস্পষ্টভাবে ডাক্তারদের অনুমতি দিতে হবে" বিবৃতিটি কি আপনি প্রসারিত করতে পারেন ? আমি উদ্বিগ্ন যে কিছু অপ্রত্যাশিত আচরণের ফলাফল হতে পারে। একটি ছোট প্রভাব সঙ্গে একই জিনিস অর্জন করার উপায় আছে?
ব্লুং

আমি প্রয়োজন আত $LINENO - 1। এটি ছাড়া সঠিকভাবে প্রদর্শন করুন।
kyb

ব্যাশ এবং zsh মধ্যে অপেক্ষাকৃত ছোট ব্যবহার উদাহরণস্বরূপfalse || die "hello death"
kyb

6

আর একটি বিবেচনা হ'ল ফিরতে প্রস্থান কোড। কেবল " 1" বেশ স্ট্যান্ডার্ড, যদিও ব্যাশ নিজেই ব্যবহার করে এমন কয়েকটি মুখ্য রিজার্ভ প্রস্থান কোড রয়েছে এবং সেই একই পৃষ্ঠায় যুক্তি দেওয়া হয়েছে যে ব্যবহারকারী-সংজ্ঞায়িত কোডগুলি সি / সি ++ মানগুলির সাথে সামঞ্জস্য করার জন্য 64-113 সীমাতে থাকা উচিত।

আপনি বিট ভেক্টর পদ্ধতির বিষয়টিও বিবেচনা করতে পারেন mount এর প্রস্থান কোডগুলির জন্য ব্যবহার করে:

 0  success
 1  incorrect invocation or permissions
 2  system error (out of memory, cannot fork, no more loop devices)
 4  internal mount bug or missing nfs support in mount
 8  user interrupt
16  problems writing or locking /etc/mtab
32  mount failure
64  some mount succeeded

ORকোডগুলি একসাথে যুক্ত করা আপনার স্ক্রিপ্টকে একাধিক একযোগে ত্রুটিগুলি সংকেত দেওয়ার অনুমতি দেয়।


4

আমি নিম্নলিখিত ট্র্যাপ কোড ব্যবহার করি, এটি পাইপ এবং 'সময়' কমান্ডের মাধ্যমে ত্রুটিগুলি সনাক্ত করার অনুমতি দেয়

#!/bin/bash
set -o pipefail  # trace ERR through pipes
set -o errtrace  # trace ERR through 'time command' and other functions
function error() {
    JOB="$0"              # job name
    LASTLINE="$1"         # line of error occurrence
    LASTERR="$2"          # error code
    echo "ERROR in ${JOB} : line ${LASTLINE} with exit code ${LASTERR}"
    exit 1
}
trap 'error ${LINENO} ${?}' ERR

5
functionশব্দ অযৌক্তিকভাবে POSIX-সঙ্গতিপূর্ণ নয়। আপনার ঘোষণাটি ঠিক error() {করার functionআগে বিবেচনা করুন no
চার্লস ডাফি

5
${$?}ঠিক হওয়া উচিত $?, বা ${?}যদি আপনি অপ্রয়োজনীয় ধনুর্বন্ধনী ব্যবহার করার জন্য জোর দিয়ে থাকেন; অভ্যন্তরীণ $ভুল।
চার্লস ডাফি

3
@ চার্লসডুফি এখনই, পসিক্স অকৃত্রিমভাবে জিএনইউ / লিনাক্স-অসম্পূর্ণ (এখনও, আমি আপনার বক্তব্য রাখছি)
ক্রড ল্যাংশন

3

আমি ব্যবহার করেছি

die() {
        echo $1
        kill $$
}

আগে; আমি মনে করি কারণ 'প্রস্থান' কোনও কারণে আমার জন্য ব্যর্থ হয়েছিল। যদিও উপরের ডিফল্টগুলি ভাল ধারণা বলে মনে হচ্ছে।


এসটিডিআরআরতে এরর-বার্তা পাঠানো আরও ভাল, না?
অ্যানকোস্টিস

3

এটি এখন কিছু সময়ের জন্য আমার ভাল সেবা করেছে। এটি ত্রুটি বা সতর্কতা বার্তাগুলি লাল রঙে, প্রতিটি প্যারামিটারে এক লাইন প্রিন্ট করে এবং একটি optionচ্ছিক প্রস্থান কোডকে অনুমতি দেয়।

# Custom errors
EX_UNKNOWN=1

warning()
{
    # Output warning messages
    # Color the output red if it's an interactive terminal
    # @param $1...: Messages

    test -t 1 && tput setf 4

    printf '%s\n' "$@" >&2

    test -t 1 && tput sgr0 # Reset terminal
    true
}

error()
{
    # Output error messages with optional exit code
    # @param $1...: Messages
    # @param $N: Exit code (optional)

    messages=( "$@" )

    # If the last parameter is a number, it's not part of the messages
    last_parameter="${messages[@]: -1}"
    if [[ "$last_parameter" =~ ^[0-9]*$ ]]
    then
        exit_code=$last_parameter
        unset messages[$((${#messages[@]} - 1))]
    fi

    warning "${messages[@]}"

    exit ${exit_code:-$EX_UNKNOWN}
}

3

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

#!/bin/bash

error_exit()
{
    if [ "$?" != "0" ]; then
        log.sh "$1"
        exit 1
    fi
}

এখন এটিকে একই স্ক্রিপ্টের মধ্যে কল করতে (বা অন্য কোনওটিতে যদি আমি ব্যবহার করি তবে export -f error_exit) আমি কেবল ফাংশনের নামটি লিখি এবং প্যারামিটার হিসাবে একটি বার্তা পাস করি, এর মতো:

#!/bin/bash

cd /home/myuser/afolder
error_exit "Unable to switch to folder"

rm *
error_exit "Unable to delete all files"

এটি ব্যবহার করে আমি কিছু স্বয়ংক্রিয় প্রক্রিয়াটির জন্য সত্যই শক্তিশালী বাশ ফাইল তৈরি করতে সক্ষম হয়েছি এবং এটি ত্রুটির ক্ষেত্রে বন্ধ হয়ে যাবে এবং আমাকে অবহিত log.shকরবে (তা করবে)


2
কার্য-সংজ্ঞা নির্ধারণের জন্য পসিক্স সিনট্যাক্সটি ব্যবহার করার বিষয়ে বিবেচনা করুন - কোনও functionকীওয়ার্ড নেই, ঠিক error_exit() {
চার্লস ডাফি

2
আপনি কেবল না করার কোনও কারণ আছে cd /home/myuser/afolder || error_exit "Unable to switch to folder"?
পিয়েরে-অলিভিয়ের ভারেসে

@ পিয়েরে-অলিভিয়ারভেয়ারস || ব্যবহার না করার কোনও বিশেষ কারণ নেই। এটি একটি বিদ্যমান কোডের কেবল একটি সংক্ষিপ্তসার ছিল এবং আমি প্রতিটি সম্পর্কিত লাইনের পরে "ত্রুটি পরিচালনা" লাইনগুলি যুক্ত করেছি। কিছু খুব দীর্ঘ এবং এটি পৃথক (তাত্ক্ষণিক) লাইনে রাখা কেবল ক্লিনার ছিল
নেলসন রড্রিগেজ

দেখে মনে হচ্ছে একটি পরিষ্কার সমাধান, শেল চেক অভিযোগ করেছে: github.com/koalaman/shellcheck/wiki/SC2181
mululse

1

অনুপস্থিত কমান্ড বা ফাংশনগুলির জন্য এই কৌশলটি কার্যকর। অনুপস্থিত ফাংশনটির নাম (বা এক্সিকিউটেবল) $ _ এ পাস করা হবে

function handle_error {
    status=$?
    last_call=$1

    # 127 is 'command not found'
    (( status != 127 )) && return

    echo "you tried to call $last_call"
    return
}

# Trap errors.
trap 'handle_error "$_"' ERR

$_ফাংশন হিসাবে একই হিসাবে পাওয়া যাবে না $?? আমি নিশ্চিত না যে ফাংশনে একটি ব্যবহার করার কোনও কারণ আছে তবে অন্যটি নয়।
শে

1

এই ফাংশনটি বরং সম্প্রতি আমাকে পরিবেশন করছে:

action () {
    # Test if the first parameter is non-zero
    # and return straight away if so
    if test $1 -ne 0
    then
        return $1
    fi

    # Discard the control parameter
    # and execute the rest
    shift 1
    "$@"
    local status=$?

    # Test the exit status of the command run
    # and display an error message on failure
    if test ${status} -ne 0
    then
        echo Command \""$@"\" failed >&2
    fi

    return ${status}
}

আপনি 0 বা শেষ রিটার্ন মানটি চালানোর জন্য কমান্ডের নামে যুক্ত করে কল করেছেন, যাতে ত্রুটির মানগুলি পরীক্ষা না করে আপনি কমান্ডগুলি শৃঙ্খলাবদ্ধ করতে পারেন। এটির সাথে, এই বিবৃতি অবরুদ্ধ:

command1 param1 param2 param3...
command2 param1 param2 param3...
command3 param1 param2 param3...
command4 param1 param2 param3...
command5 param1 param2 param3...
command6 param1 param2 param3...

এটি হয়ে:

action 0 command1 param1 param2 param3...
action $? command2 param1 param2 param3...
action $? command3 param1 param2 param3...
action $? command4 param1 param2 param3...
action $? command5 param1 param2 param3...
action $? command6 param1 param2 param3...

<<<Error-handling code here>>>

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


0

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

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

#!/bin/sh

# prints:
#
# --> outer
# --> inner
# ./so_1.sh: line 16: some_failed_command: command not found
# <-- inner
# <-- outer

set -e

outer() {
  echo '--> outer'
  (inner) || {
    exit_code=$?
    echo '--> cleanup'
    return $exit_code
  }
  echo '<-- outer'
}

inner() {
  set -e
  echo '--> inner'
  some_failed_command
  echo '<-- inner'
}

outer

তবে ||ক্লিনআপের আগে বাইরের ফাংশন থেকে ফিরে আসা রোধ করার জন্য অপারেটরের প্রয়োজন। কৌশলটি পটভূমিতে অভ্যন্তরীণ কমান্ডটি চালানো এবং তারপরে তাত্ক্ষণিকভাবে অপেক্ষা করুন wait waitBuiltin ভেতরের কমান্ডের প্রস্থান কোড ফিরে আসবে, এবং এখন আপনি ব্যবহার করছেন ||পরে wait, না ভেতরের ফাংশন, তাই set -eআধুনিক ভিতরে সঠিকভাবে কাজ করে:

#!/bin/sh

# prints:
#
# --> outer
# --> inner
# ./so_2.sh: line 27: some_failed_command: command not found
# --> cleanup

set -e

outer() {
  echo '--> outer'
  inner &
  wait $! || {
    exit_code=$?
    echo '--> cleanup'
    return $exit_code
  }
  echo '<-- outer'
}

inner() {
  set -e
  echo '--> inner'
  some_failed_command
  echo '<-- inner'
}

outer

এখানে জেনেরিক ফাংশন যা এই ধারণার উপর নির্ভর করে। আপনি localকীওয়ার্ডগুলি সরিয়ে ফেললে সমস্ত পসিক্স-সামঞ্জস্যপূর্ণ শেলগুলিতে কাজ করা উচিত , অর্থাত্ সমস্তগুলি local x=yকেবলমাত্র দিয়ে প্রতিস্থাপন করুন x=y:

# [CLEANUP=cleanup_cmd] run cmd [args...]
#
# `cmd` and `args...` A command to run and its arguments.
#
# `cleanup_cmd` A command that is called after cmd has exited,
# and gets passed the same arguments as cmd. Additionally, the
# following environment variables are available to that command:
#
# - `RUN_CMD` contains the `cmd` that was passed to `run`;
# - `RUN_EXIT_CODE` contains the exit code of the command.
#
# If `cleanup_cmd` is set, `run` will return the exit code of that
# command. Otherwise, it will return the exit code of `cmd`.
#
run() {
  local cmd="$1"; shift
  local exit_code=0

  local e_was_set=1; if ! is_shell_attribute_set e; then
    set -e
    e_was_set=0
  fi

  "$cmd" "$@" &

  wait $! || {
    exit_code=$?
  }

  if [ "$e_was_set" = 0 ] && is_shell_attribute_set e; then
    set +e
  fi

  if [ -n "$CLEANUP" ]; then
    RUN_CMD="$cmd" RUN_EXIT_CODE="$exit_code" "$CLEANUP" "$@"
    return $?
  fi

  return $exit_code
}


is_shell_attribute_set() { # attribute, like "x"
  case "$-" in
    *"$1"*) return 0 ;;
    *)    return 1 ;;
  esac
}

ব্যবহারের উদাহরণ:

#!/bin/sh
set -e

# Source the file with the definition of `run` (previous code snippet).
# Alternatively, you may paste that code directly here and comment the next line.
. ./utils.sh


main() {
  echo "--> main: $@"
  CLEANUP=cleanup run inner "$@"
  echo "<-- main"
}


inner() {
  echo "--> inner: $@"
  sleep 0.5; if [ "$1" = 'fail' ]; then
    oh_my_god_look_at_this
  fi
  echo "<-- inner"
}


cleanup() {
  echo "--> cleanup: $@"
  echo "    RUN_CMD = '$RUN_CMD'"
  echo "    RUN_EXIT_CODE = $RUN_EXIT_CODE"
  sleep 0.3
  echo '<-- cleanup'
  return $RUN_EXIT_CODE
}

main "$@"

উদাহরণ চলছে:

$ ./so_3 fail; echo "exit code: $?"

--> main: fail
--> inner: fail
./so_3: line 15: oh_my_god_look_at_this: command not found
--> cleanup: fail
    RUN_CMD = 'inner'
    RUN_EXIT_CODE = 127
<-- cleanup
exit code: 127

$ ./so_3 pass; echo "exit code: $?"

--> main: pass
--> inner: pass
<-- inner
--> cleanup: pass
    RUN_CMD = 'inner'
    RUN_EXIT_CODE = 0
<-- cleanup
<-- main
exit code: 0

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

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