ट्रैप, ईआरआर, और त्रुटि रेखा को प्रतिध्वनित करना


30

मैं सभी त्रुटियों पर एक फ़ंक्शन को कॉल करने के लिए ट्रैप का उपयोग करके कुछ त्रुटि रिपोर्ट बनाने की कोशिश कर रहा हूं:

Trap "_func" ERR

क्या ईआरआर सिग्नल को किस लाइन से भेजा जाना संभव है? खोल बैश है।

यदि मैं ऐसा करता हूं, तो मैं पढ़ सकता हूं और रिपोर्ट कर सकता हूं कि किस कमांड का उपयोग किया गया था और कुछ कार्यों को लॉग / इन करें।

या शायद मैं यह सब गलत कर रहा हूँ?

मैंने निम्नलिखित के साथ परीक्षण किया:

#!/bin/bash
trap "ECHO $LINENO" ERR

echo hello | grep "asdf"

और $LINENOलौट रहा है 2. काम नहीं कर रहा है।


आप बैश डीबगर स्क्रिप्ट देख सकते हैं bashdb। ऐसा लगता है कि पहले तर्क में trapउन चर शामिल हो सकते हैं जिनका वांछित संदर्भ में मूल्यांकन किया गया है। इसलिए trap 'echo $LINENO' ERR'काम करना चाहिए।
donothingsuccessfully

हम्म सिर्फ एक बुरा गूंज के साथ यह कोशिश की | grep कमांड और यह ट्रैप स्टेटमेंट की लाइन लौटाता है। लेकिन मैं bashdb पर एक नज़र डालूंगा
मेकफ्लैश

मुझे खेद है ... मैंने अपने मूल प्रश्न में निर्दिष्ट नहीं किया कि मुझे एक देशी समाधान की आवश्यकता है। मैंने प्रश्न संपादित किया।
मेकफ्लैश

क्षमा करें, मैंने उदाहरण पंक्ति का उल्लेख किया है trap 'echo $LINENO' ERR:। पहला तर्क trapपूरे echo $LINENOहार्डकॉट का है। यह बाश में है।
donothingsuccessfully

5
@Mechaflash trap 'echo $LINENO' ERRसिंगल कोट्स के साथ होना चाहिए , डबल कोट्स के साथ नहीं। आपके द्वारा लिखी गई कमांड के साथ, $LINENOपंक्ति 2 के पार्स होने पर विस्तार किया जाता है, इसलिए जाल echo 2(या बल्कि ECHO 2, जो आउटपुट होगा bash: ECHO: command not found) है।
गाइल्स 'एसओ- बुराई को रोकें'

जवाबों:


61

जैसा कि टिप्पणियों में बताया गया है, आपका उद्धरण गलत है। $LINENOजब जाल रेखा पहले पार्स हो जाती है तो आपको विस्तार से रोकने के लिए एकल उद्धरणों की आवश्यकता होती है।

यह काम:

#! /bin/bash

err_report() {
    echo "Error on line $1"
}

trap 'err_report $LINENO' ERR

echo hello | grep foo  # This is line number 9

इसे चलाना:

 $ ./test.sh
 Error on line 9

फ़ंक्शन कॉल के साथ उदाहरण के लिए धन्यवाद। मुझे नहीं पता था कि इस मामले में डबल कोट्स ने वैरिएबल का विस्तार किया है।
मचाफलेश

echo hello | grep fooमेरे लिए त्रुटि फेंकने के लिए प्रतीत नहीं होता है। क्या मैं कुछ गलत समझ रहा हूँ?
जियोथेट्री

@geotheory मेरे सिस्टम grepमें 0 से बाहर निकलने की स्थिति है अगर कोई मैच हुआ, तो 1 मैच नहीं हुआ और 1 त्रुटि के लिए। आप अपने सिस्टम पर व्यवहार की जाँच कर सकते हैंecho hello | grep foo; echo $?
पैट्रिक

नहीं, आप सही कह रहे हैं कि यह एक त्रुटि है :)
भू

कमांड विफलता पर त्रुटि का कारण बनने के लिए क्या आपको इनवोकेशन लाइन का उपयोग करने की आवश्यकता नहीं है? वह है: #! / Bin / bash -e?
टिम बर्ड

14

आप बैश बिलियन 'कॉलर' का भी उपयोग कर सकते हैं:

#!/bin/bash

err_report() {
  echo "errexit on line $(caller)" >&2
}

trap err_report ERR

echo hello | grep foo

यह फ़ाइल नाम भी प्रिंट करता है:

$ ./test.sh
errexit on line 9 ./test.sh

6

मैं @Mat द्वारा दिए गए उत्तर को वास्तव में पसंद करता हूं। इस पर बिल्डिंग, मैंने थोड़ा सहायक लिखा जो त्रुटि के लिए थोड़ा अधिक संदर्भ देता है:

हम उस लाइन की स्क्रिप्ट का निरीक्षण कर सकते हैं जो विफलता का कारण बनी:

err() {
    echo "Error occurred:"
    awk 'NR>L-4 && NR<L+4 { printf "%-5d%3s%s\n",NR,(NR==L?">>>":""),$0 }' L=$1 $0
}
trap 'err $LINENO' ERR

यहाँ यह एक छोटी परीक्षण स्क्रिप्ट में है:

#!/bin/bash

set -e

err() {
    echo "Error occurred:"
    awk 'NR>L-4 && NR<L+4 { printf "%-5d%3s%s\n",NR,(NR==L?">>>":""),$0 }' L=$1 $0
}
trap 'err $LINENO' ERR

echo one
echo two
echo three
echo four
false
echo five
echo six
echo seven
echo eight

जब हम इसे चलाते हैं तो हमें यह मिलता है:

$ /tmp/test.sh
one
two
three
four
Error occurred:
12      echo two
13      echo three
14      echo four
15   >>>false
16      echo five
17      echo six
18      echo seven

यह बेहतर होगा $(caller)कि संदर्भ देने के लिए डेटा का उपयोग किया जाए, भले ही विफलता वर्तमान स्क्रिप्ट में न हो, लेकिन इसके आयात में से एक है। हालांकि बहुत अच्छा!
त्रिकसे

2

अन्य उत्तर से प्रेरित, यहाँ एक सरल संदर्भ त्रुटि हैंडलर है:

trap '>&2 echo Command failed: $(tail -n+$LINENO $0 | head -n1)' ERR

यदि आवश्यक हो तो आप पूंछ और सिर के बजाय awk का भी उपयोग कर सकते हैं ।


1
एक कारण है कि अन्य उत्तर 3 लाइनों के ऊपर और आक्रामक रेखा के नीचे 3 लाइनों के संदर्भ प्रदान करता है - क्या होगा यदि त्रुटि एक निरंतरता रेखा से निकलती है?
इरुवर

@iruvar यह समझा जाता है, लेकिन मुझे उस अतिरिक्त संदर्भ की कोई आवश्यकता नहीं है; संदर्भ की एक पंक्ति जितनी सरल है, उतनी ही सरल है और जितनी मुझे आवश्यकता है
sanmai

ठीक है मेरे दोस्त, + 1
इरुवर

1

यहाँ एक और संस्करण है, जो @sanmai और @unpythonic से प्रेरित है। यह त्रुटि के चारों ओर स्क्रिप्ट लाइनों, रेखा संख्याओं और निकास स्थिति से पता चलता है - पूंछ और सिर का उपयोग करना जो कि awk समाधान की तुलना में सरल लगता है।

पठनीयता के लिए इसे दो लाइनों के रूप में दिखाना - आप चाहें तो इन लाइनों को एक में शामिल कर सकते हैं (संरक्षित कर सकते हैं ;):

trap 'echo >&2 "Error - exited with status $? at line $LINENO:"; 
         pr -tn $0 | tail -n+$((LINENO - 3)) | head -n7' ERR

यह set -euo pipefail( अनौपचारिक सख्त मोड ) के साथ काफी अच्छी तरह से काम करता है - कोई भी अपरिभाषित चर त्रुटि ERRछद्म संकेत को फायर किए बिना एक लाइन नंबर देता है , लेकिन अन्य मामले संदर्भ दिखाते हैं।

उदाहरण आउटपुट:

myscript.sh: line 27: blah: command not found
Error - exited with status 127 at line 27:
   24   # Do something
   25   lines=$(wc -l /etc/passwd)
   26   # More stuff
   27   blah
   28   
   29   # Check time
   30   time=$(date)

0

क्या ईआरआर सिग्नल को किस लाइन से भेजा जाना संभव है?

हां, LINENOऔर BASH_LINENOचरों को विफलता की रेखा और उस तक पहुंचने वाली रेखाओं के लिए उपयोगी माना जाता है।

या शायद मैं यह सब गलत कर रहा हूँ?

नहीं, बस -qgrep के साथ लापता विकल्प ...

echo hello | grep -q "asdf"

... -qविकल्प के लिए और के लिए grepवापस आ जाएगी । और बैश में यह नहीं है ...0true1falsetrapTrap

trap "_func" ERR

... मुझे एक देशी समाधान चाहिए ...

यहां एक ट्रॉपर है कि हां उन चीजों को डिबग करने के लिए उपयोगी हो सकता है जिनके पास थोड़ा और अधिक चक्रीय जटिलता है ...

failure.sh

## Outputs Front-Mater formatted failures for functions not returning 0
## Use the following line after sourcing this file to set failure trap
##    trap 'failure "LINENO" "BASH_LINENO" "${BASH_COMMAND}" "${?}"' ERR
failure(){
    local -n _lineno="${1:-LINENO}"
    local -n _bash_lineno="${2:-BASH_LINENO}"
    local _last_command="${3:-${BASH_COMMAND}}"
    local _code="${4:-0}"

    ## Workaround for read EOF combo tripping traps
    if ! ((_code)); then
        return "${_code}"
    fi

    local _last_command_height="$(wc -l <<<"${_last_command}")"

    local -a _output_array=()
    _output_array+=(
        '---'
        "lines_history: [${_lineno} ${_bash_lineno[*]}]"
        "function_trace: [${FUNCNAME[*]}]"
        "exit_code: ${_code}"
    )

    if [[ "${#BASH_SOURCE[@]}" -gt '1' ]]; then
        _output_array+=('source_trace:')
        for _item in "${BASH_SOURCE[@]}"; do
            _output_array+=("  - ${_item}")
        done
    else
        _output_array+=("source_trace: [${BASH_SOURCE[*]}]")
    fi

    if [[ "${_last_command_height}" -gt '1' ]]; then
        _output_array+=(
            'last_command: ->'
            "${_last_command}"
        )
    else
        _output_array+=("last_command: ${_last_command}")
    fi

    _output_array+=('---')
    printf '%s\n' "${_output_array[@]}" >&2
    exit ${_code}
}

... और फ़ंक्शन ट्रेसिंग के लिए उपरोक्त जाल सेट करने के लिए सूक्ष्म अंतरों को उजागर करने के लिए एक उदाहरण उपयोग स्क्रिप्ट भी ...

example_usage.sh

#!/usr/bin/env bash

set -E -o functrace

## Optional, but recommended to find true directory this script resides in
__SOURCE__="${BASH_SOURCE[0]}"
while [[ -h "${__SOURCE__}" ]]; do
    __SOURCE__="$(find "${__SOURCE__}" -type l -ls | sed -n 's@^.* -> \(.*\)@\1@p')"
done
__DIR__="$(cd -P "$(dirname "${__SOURCE__}")" && pwd)"


## Source module code within this script
source "${__DIR__}/modules/trap-failure/failure.sh"

trap 'failure "LINENO" "BASH_LINENO" "${BASH_COMMAND}" "${?}"' ERR


something_functional() {
    _req_arg_one="${1:?something_functional needs two arguments, missing the first already}"
    _opt_arg_one="${2:-SPAM}"
    _opt_arg_two="${3:0}"
    printf 'something_functional: %s %s %s' "${_req_arg_one}" "${_opt_arg_one}" "${_opt_arg_two}"
    ## Generate an error by calling nothing
    "${__DIR__}/nothing.sh"
}


## Ignoring errors prevents trap from being triggered
something_functional || echo "Ignored something_functional returning $?"
if [[ "$(something_functional 'Spam!?')" == '0' ]]; then
    printf 'Nothing somehow was something?!\n' >&2 && exit 1
fi


## And generating an error state will cause the trap to _trace_ it
something_functional '' 'spam' 'Jam'

ऊपर जहां बैश संस्करण 4+ पर परीक्षण किया गया है, इसलिए यदि चार से पहले के संस्करणों के लिए कुछ आवश्यक है, तो एक टिप्पणी छोड़ दें, या चार के न्यूनतम संस्करण के साथ सिस्टम पर विफलताओं को विफल करने के लिए एक समस्या खोलें

मुख्य takeaways हैं ...

set -E -o functrace
  • -Eकरने के लिए काम करता है के भीतर त्रुटियों का कारण बनता बबल सामने

  • -o functrace जब किसी फ़ंक्शन के भीतर कुछ विफल हो जाता है तो अधिक वर्बोसिटी के लिए अनुमति देता है

trap 'failure "LINENO" "BASH_LINENO" "${BASH_COMMAND}" "${?}"' ERR
  • सिंगल कॉट्स का उपयोग फंक्शन कॉल के चारों ओर किया जाता है और डबल कोट्स व्यक्तिगत तर्कों के आसपास होते हैं

  • वर्तमान मानों के बजाय संदर्भ LINENOऔर BASH_LINENOपारित किए जाते हैं, हालांकि इसे जाल से जुड़े बाद के संस्करणों में छोटा किया जा सकता है, जैसे कि अंतिम विफलता रेखा इसे आउटपुट में बनाती है

  • पहले BASH_COMMANDऔर बाहर निकलने की स्थिति ( $?) के मान पारित किए जाते हैं, पहले एक त्रुटि वापस करने वाली कमांड प्राप्त करने के लिए, और दूसरा यह सुनिश्चित करने के लिए कि ट्रैप गैर-त्रुटि स्थितियों पर ट्रिगर नहीं होता है

और जब कुछ लोग असहमत हो सकते हैं, तो मुझे लगता है कि एक आउटपुट एरे का निर्माण करना आसान है और प्रत्येक एलाइन तत्व को प्रिंट करने के लिए प्रिंटफ का उपयोग करना चाहिए।

printf '%s\n' "${_output_array[@]}" >&2

... >&2अंत में बिट भी त्रुटियों का कारण बनता है जहां उन्हें जाना चाहिए (मानक त्रुटि), और बस त्रुटियों को कैप्चर करने की अनुमति देता है ...

## ... to a file...
some_trapped_script.sh 2>some_trapped_errros.log

## ... or by ignoring standard out...
some_trapped_script.sh 1>/dev/null

जैसा कि स्टैक ओवरफ्लो पर इन और अन्य उदाहरणों द्वारा दिखाया गया है , उपयोगिताओं में निर्मित डिबगिंग सहायता का निर्माण करने के बहुत सारे तरीके हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.