कैसे एक लाइन बश स्क्रिप्ट में एक त्रुटि पकड़ने के लिए?


13

मैंने निम्नलिखित स्क्रिप्ट बनाई:

# !/bin/bash

# OUTPUT-COLORING
red='\e[0;31m'
green='\e[0;32m'
NC='\e[0m' # No Color

# FUNCTIONS
# directoryExists - Does the directory exist?
function directoryExists {
    cd $1
    if [ $? = 0 ]
            then
                    echo -e "${green}$1${NC}"
            else
                    echo -e "${red}$1${NC}"
    fi
}

# EXE
directoryExists "~/foobar"
directoryExists "/www/html/drupal"

स्क्रिप्ट काम करती है, लेकिन मेरी गूँज के बगल में, आउटपुट भी है जब

cd $1

निष्पादन पर विफल।

testscripts//test_labo3: line 11: cd: ~/foobar: No such file or directory

क्या इसे पकड़ना संभव है?


सिर्फ एक FYI करें, आप इसे बहुत सरल भी कर सकते हैं; test -d /path/to/directory(या [[ -d /path/to/directory ]]बैश में) आपको बताएगा कि दिया गया लक्ष्य एक निर्देशिका है या नहीं, और यह चुपचाप करेगा।
पैट्रिक

@ पैट्रिक, कि अगर यह एक निर्देशिका है, तो बस परीक्षण करता है, यदि आप इसमें नहीं हो सकते cd
स्टीफन चेज़लस

@StephaneChazelas हाँ। समारोह का नाम है directoryExists
पैट्रिक

यहां एक विस्तृत जवाब देखें: बैश स्क्रिप्ट में त्रुटि उठाएं
कोडवर्ड

जवाबों:


8

जैसे ही यह चलता है, आपकी स्क्रिप्ट निर्देशिका बदल जाती है, जिसका अर्थ है कि यह सापेक्ष पथनामों की एक श्रृंखला के साथ काम नहीं करेगा। आपने बाद में टिप्पणी की कि आप केवल निर्देशिका अस्तित्व की जांच करना चाहते थे, न कि उपयोग करने की क्षमता cd, इसलिए उत्तर को उपयोग करने की आवश्यकता नहीं है cd। संशोधित। का उपयोग कर tput और रंगों से man terminfo:

#!/bin/bash -u
# OUTPUT-COLORING
red=$( tput setaf 1 )
green=$( tput setaf 2 )
NC=$( tput setaf 0 )      # or perhaps: tput sgr0

# FUNCTIONS
# directoryExists - Does the directory exist?
function directoryExists {
    # was: do the cd in a sub-shell so it doesn't change our own PWD
    # was: if errmsg=$( cd -- "$1" 2>&1 ) ; then
    if [ -d "$1" ] ; then
        # was: echo "${green}$1${NC}"
        printf "%s\n" "${green}$1${NC}"
    else
        # was: echo "${red}$1${NC}"
        printf "%s\n" "${red}$1${NC}"
        # was: optional: printf "%s\n" "${red}$1 -- $errmsg${NC}"
    fi
}

( पाठ में भागने के अनुक्रम पर कार्य कर सकने printfवाले समस्याग्रस्त के बजाय अधिक अमूर्त का उपयोग करने के लिए संपादित echo।)


यह भी ठीक करता है (जब तक कि xpg_echo चालू न हो) जब फ़ाइलनाम में बैकलैश वर्ण होते हैं।
स्टीफन चेज़लस

12

set -eएग्जिट-ऑन-एरर मोड सेट करने के लिए उपयोग करें : यदि एक साधारण कमांड नॉनजरो स्टेटस (विफलता का संकेत) देता है, तो शेल बाहर निकलता है।

खबरदार जो set -eहमेशा किक नहीं करता है। परीक्षण पदों में कमांड को विफल करने की अनुमति दी जाती है (जैसे if failing_command, failing_command || fallback)। उपधारा में शामिल केवल उपधारा से बाहर निकलने की ओर जाता है, न कि अभिभावक: set -e; (false); echo fooप्रदर्शित करता है foo

वैकल्पिक रूप से, या इसके अलावा, बैश (और ksh और zsh, लेकिन सादे श) में नहीं, आप एक कमांड निर्दिष्ट कर सकते हैं जो कि एक कमांड के नॉनस्टेरो स्टेटस में ERRफंसने पर, उदाहरण के लिए निष्पादित किया जाता है trap 'err=$?; echo >&2 "Exiting on error $err"; exit $err' ERR। ध्यान दें कि जैसे मामलों में (false); …, ERR ट्रैप को सबस्क्रिप्शन में निष्पादित किया जाता है, इसलिए यह माता-पिता के बाहर निकलने का कारण नहीं बन सकता है।


हाल ही में मैंने एक छोटा सा प्रयोग किया और ||व्यवहार को ठीक करने का एक सुविधाजनक तरीका खोजा , जो जाल का उपयोग किए बिना आसानी से उचित त्रुटि से निपटने में सक्षम बनाता है। मेरा जवाब देखिए । आप उस तरीके के बारे में क्या सोचते हैं?
skozin

@ sam.kozin आपके पास विस्तार से आपके उत्तर की समीक्षा करने का समय नहीं है, यह सिद्धांत पर अच्छा लगता है। पोर्टेबिलिटी के अलावा, ksh / bash / zsh के ERR ट्रैप पर क्या लाभ हैं?
गाइल्स का SO- बुराई पर रोक '17

संभवतया एकमात्र लाभ कंपोजिटिबिलिटी है, क्योंकि आप एक अन्य ट्रैप को ओवरराइट करने का जोखिम नहीं उठाते हैं जो आपके कार्य करने से पहले निर्धारित किया गया था। यह एक उपयोगी विशेषता है जब आप कुछ सामान्य फ़ंक्शन लिख रहे हैं जिसे आप बाद में स्रोत और अन्य लिपियों से उपयोग करेंगे। एक और लाभ पूर्ण POSIX संगतता हो सकता है, हालांकि यह इतना महत्वपूर्ण नहीं है क्योंकि ERRछद्म संकेत सभी प्रमुख गोले में समर्थित है। समीक्षा के लिए धन्यवाद! =)
skozin

@ sam.kozin मैं अपनी पिछली टिप्पणी में लिखना भूल गया: आप इसे कोड समीक्षा पर पोस्ट कर सकते हैं और चैटरूम में एक लिंक पोस्ट कर सकते हैं ।
गिल्स एसओ- बुराई को रोकना '

सुझाव के लिए धन्यवाद, मैं इसका पालन करने की कोशिश करूंगा। कोड समीक्षा के बारे में नहीं जानते थे।
skozin

6

@ उत्तर के उत्तर पर विस्तार करने के लिए :

वास्तव में, 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

लेकिन ||सफाई से पहले बाहरी फ़ंक्शन से लौटने से रोकने के लिए ऑपरेटर की आवश्यकता होती है।

एक छोटी सी चाल है जो इसे ठीक करने के लिए इस्तेमाल की जा सकती है: पृष्ठभूमि में आंतरिक कमांड चलाएं, और फिर तुरंत इसके लिए प्रतीक्षा करें। 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कीवर्ड हटाते हैं , तो सभी POSIX- संगत गोले में काम करना चाहिए , अर्थात सभी 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कॉलिंग फ़ंक्शन को प्रचारित नहीं करेंगे, क्योंकि कमांड एक सबस्क्रिप्शन में चलता है।


2

आप यह नहीं कहते हैं कि वास्तव में आपका क्या मतलब है catch--- रिपोर्ट और जारी रखें; आगे की प्रक्रिया को निरस्त करें?

चूंकि cdविफलता पर एक गैर-शून्य स्थिति देता है, आप कर सकते हैं:

cd -- "$1" && echo OK || echo NOT_OK

आप बस विफलता पर बाहर निकल सकते हैं:

cd -- "$1" || exit 1

या, अपने संदेश और निकास की प्रतिध्वनि करें:

cd -- "$1" || { echo NOT_OK; exit 1; }

और / या cdविफलता द्वारा प्रदान की गई त्रुटि को दबा देती है :

cd -- "$1" 2>/dev/null || exit 1

मानकों के अनुसार, कमांड को STDERR (फाइल डिस्क्रिप्टर 2) पर त्रुटि संदेश डालना चाहिए। इस प्रकार 2>/dev/nullSTDERR को "बिट-बकेट" द्वारा पुनर्निर्देशित किया जाता है /dev/null

(अपने चर को उद्धृत करने और विकल्पों के अंत को चिह्नित करने के लिए मत भूलना cd)।


@Stephane चेज़लस ने एंड-ऑफ़-ऑप्शन को अच्छी तरह से लिया गया है। संपादन के लिए धन्यवाद।
JRFerguson

1

वास्तव में आपके मामले के लिए मैं कहूंगा कि तर्क में सुधार किया जा सकता है।

सीडी के बजाय और फिर जांचें कि क्या यह मौजूद है, जांचें कि क्या यह मौजूद है तो निर्देशिका में जाएं।

if [ -d "$1" ]
then
     printf "${green}${NC}\\n" "$1"
     cd -- "$1"
else 
     printf "${red}${NC}\\n" "$1"
fi  

लेकिन अगर आपका उद्देश्य संभावित त्रुटियों को शांत करना है cd -- "$1" 2>/dev/null, लेकिन यह आपको भविष्य में कठिन बना देगा। आप देख सकते हैं कि क्या परीक्षण के झंडे यहां हैं: यदि दस्तावेज हो तो बैश करें :


यह उत्तर $1चर को उद्धृत करने में विफल रहता है और विफल हो जाएगा यदि उस चर में रिक्त या अन्य शेल मेटाचैकर शामिल हैं। यह जांचने में भी विफल रहता है कि उपयोगकर्ता के पास इसकी अनुमति है या cdनहीं।
इयान डी। एलन

मैं वास्तव में यह जांचने की कोशिश कर रहा था कि क्या एक निश्चित निर्देशिका मौजूद थी, जरूरी नहीं कि वह सीडी हो। लेकिन क्योंकि मैं बेहतर नहीं जानता था, मैंने सोचा था कि अगर यह मौजूद नहीं है तो इसे पकड़ने के लिए सीडी की कोशिश एक त्रुटि का कारण बनेगी? मैं [-d $ 1] के बारे में नहीं जानता था कि वास्तव में मुझे इसकी आवश्यकता है। तो, बहुत-बहुत धन्यवाद! (मैं जावा को proram करने के लिए उपयोग किया जाता हूं, और अगर एक विवरण में निर्देशिका के लिए जाँच करना जावा में बिल्कुल सामान्य नहीं है)
थॉमस डे वाइल्ड
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.