কিভাবে একটি লিনাক্স ব্যাশ স্ক্রিপ্টে ত্রুটি ধরা যায়?


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

এটা কি ধরা সম্ভব?


কেবলমাত্র একটি এফওয়াইআই, আপনি এটি আরও অনেক সহজ করতে পারেন; test -d /path/to/directory(বা [[ -d /path/to/directory ]]ব্যাশে) আপনাকে প্রদত্ত টার্গেটটি ডিরেক্টরি কিনা তা আপনাকে জানাবে এবং এটি চুপচাপ এটি করবে।
প্যাট্রিক

@ পেট্রিক, এটি পরীক্ষা করে যদি এটি ডিরেক্টরি হয় তবে আপনি এটিতে পারছেন কিনা তা নয় cd
স্টাফেন চেজেলাস

পুনঃটুইট ফাংশন নাম 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সর্বদা লাথি না দেয় test পরীক্ষার পদগুলিতে কমান্ডগুলিকে ব্যর্থ হওয়ার অনুমতি দেওয়া হয় (যেমন if failing_command, failing_command || fallback)। সাবশেলের কমান্ডগুলি কেবল সাবশেল থেকে বেরিয়ে আসে, পিতামাতাকে নয়: set -e; (false); echo fooপ্রদর্শন করে foo

অন্যথা, বা উপরন্তু, ব্যাশ (ও ksh এবং zsh, কিন্তু না প্লেইন SH), আপনি একটি কমান্ড যে ক্ষেত্রে মৃত্যুদন্ড কার্যকর হচ্ছে কমান্ড দিয়ে একটি অশূন্য অবস্থা ফেরৎ, নির্দিষ্ট করতে পারেন ERRফাঁদ যেমন trap 'err=$?; echo >&2 "Exiting on error $err"; exit $err' ERR। মনে রাখবেন যে এর মতো ক্ষেত্রে (false); …ERR ট্র্যাপটি সাব-শেলের মধ্যে কার্যকর করা হয়, সুতরাং এটি পিতামাতাকে প্রস্থান করতে পারে না।


সম্প্রতি আমি সামান্য পরীক্ষা-নিরীক্ষা করেছি এবং ||আচরণের ফিক্সিংয়ের একটি সুবিধাজনক উপায় আবিষ্কার করেছি , যা ফাঁদ ব্যবহার না করে সহজেই সঠিক ত্রুটি পরিচালনা করতে সক্ষম করে। আমার উত্তর দেখুন । আপনি এই পদ্ধতি সম্পর্কে কি মনে করেন?
স্কোজিন

@ সাম.কোজিন আপনার উত্তরটি বিশদভাবে পর্যালোচনা করার মতো সময় আমার কাছে নেই, এটি নীতিগতভাবে ভাল দেখায়। বহনযোগ্যতা ছাড়াও, ksh / bash / zsh এর ERR ফাঁদে কী কী সুবিধা রয়েছে?
গিলস 'অসন্তুষ্ট হওয়া বন্ধ করুন'

সম্ভবত একমাত্র উপকারিতা হ'ল সামঞ্জস্যতা, যেহেতু আপনি চালানোর আগে সেট করা আরেকটি ট্র্যাপ ওভাররাইট করার ঝুঁকি নেই। কোন কার্যকর বৈশিষ্ট্য আপনি যখন কিছু সাধারণ ফাংশন লিখছেন যা আপনি পরে উত্স এবং অন্যান্য স্ক্রিপ্ট থেকে ব্যবহার করবেন। আর একটি সুবিধা পুরো পসিক্স সামঞ্জস্যতা হতে পারে, যদিও এটি এতটা গুরুত্বপূর্ণ নয় যেহেতু ERRসমস্ত বড় শেলগুলিতে সিউডো সিগন্যাল সমর্থিত supported পর্যালোচনার জন্য ধন্যবাদ! =)
স্কোজিন

@ sam.kozin আমি আমার আগের মন্তব্যে লিখতে ভুলে গিয়েছি: আপনি কোড রিভিউতে পোস্ট করতে এবং চ্যাটরুমে একটি লিঙ্ক পোস্ট করতে চাইতে পারেন ।
গিলস 'অসন্তুষ্ট হওয়া বন্ধ করুন'

পরামর্শের জন্য ধন্যবাদ, আমি এটি অনুসরণ করার চেষ্টা করব। কোড পর্যালোচনা সম্পর্কে জানতেন না।
স্কোজিন

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কীওয়ার্ডগুলি মুছে ফেললে সমস্ত পসিক্স-সামঞ্জস্যপূর্ণ শেলগুলিতে কাজ করা উচিত , অর্থাত্ সমস্তগুলি 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

মানদণ্ড অনুসারে, কমান্ডগুলিতে এসটিডিআরআর (ফাইলের বর্ণনাকারী 2) এ ত্রুটি বার্তা রাখা উচিত। এইভাবে 2>/dev/nullSTDERR দ্বারা পরিচিত "বিট-বালতি" এ পুনঃনির্দেশ বলে /dev/null

(আপনার ভেরিয়েবলগুলি উদ্ধৃত করতে এবং এর বিকল্পগুলির শেষ চিহ্নিত করতে ভুলবেন না cd)।


@ স্টেফেন চেজেলাস অফ-অফ-অপশনগুলি উদ্ধৃত করা এবং সিগন্যাল করার পয়েন্টটি ভালভাবে নেওয়া হয়েছে। সম্পাদনার জন্য ধন্যবাদ।
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] ঠিক তেমন প্রয়োজন আমার সম্পর্কে জানতাম না। সুতরাং, আপনাকে অনেক ধন্যবাদ! (আমি জাভা প্রম করতে ব্যবহৃত হয়েছি, এবং জাভা-তে স্টেটমেন্টের বিবরণে ডিরেক্টরিটি পরীক্ষা করা ঠিক তেমন সাধারণ নয়)
টমাস ডি উইল্ড
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.