Linux bash skriptidagi xatoni qanday olish mumkin?

Men quyidagi buyruqni yaratdim:

# !/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"

Buyruq fayli ishlaydi, lekin mening echimlarim bilan birga, chiqdi ham bor

cd $1

ijro etilmasa.

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

Buni qo'lga olish mumkinmi?

9
Faqat FYI, siz buni juda oddiy qilishingiz mumkin; (yoki [[-d/path/to/directory /]]] -yoqidagi test -d/path/to/katalog yoki bo'lmasa, u jim bo'lib qoladi.
qo'shib qo'ydi muallif ramayac, manba
@StephaneChazelas ha. Funktsiya nomi directoryExists hisoblanadi.
qo'shib qo'ydi muallif ramayac, manba
@Patrick, agar u faqatgina katalog bo'lsa, uni tekshirib ko'radi, agar siz unga cd ni kiritishingiz mumkin bo'lsa.
qo'shib qo'ydi muallif Stéphane Chazelas, manba
Bu yerda batafsil javobni ko'ring: Bash skriptidagi xatoni ko'taring .
qo'shib qo'ydi muallif codeforester, manba

5 javoblar

Chiqishning xato rejimini o'rnatish uchun set -e -ni ishlating: agar oddiy buyruqlar nolga tengsiz holatni (qobiliyatsizligini bildiradi) qaytaradigan bo'lsa, qobiq chiqadi.

set -e har doim ham kirishiga e'tibor bering. Test joylarida buyruqlar (masalan, agar failing_command , failing_command || fallback ). Subshell buyruqlar faqat ota-ona emas, balki pastki qismdan chiqishiga olib keladi: set -e; (noto'g'ri); echo foo belgisi foo ni ko'rsatadi.

Alternatively, or in addition, in bash (and ksh and zsh, but not plain sh), you can specify a command that's executed in case a command returns a nonzero status, with the ERR trap, e.g. trap 'err=$?; echo >&2 "Exiting on error $err"; exit $err' ERR. Note that in cases like (false); …, the ERR trap is executed in the subshell, so it can't cause the parent to exit.

8
qo'shib qo'ydi
@ sam.kozin Sizning javobingizni batafsil ko'rib chiqish uchun vaqtim yo'q, bu printsipda yaxshi ko'rinadi. Taşınabilirlik tashqari, ksh/bash/zsh'nin ERR tuzog'iga nisbatan qanday afzalliklar bor?
qo'shib qo'ydi muallif Valters Vingolds, manba
@ sam.kozin Men avvalgi izohimda yozishni unutgan edim: buni Kodni tekshirish ustiga joylashtirishingiz mumkin va suhbat xonasi .
qo'shib qo'ydi muallif Valters Vingolds, manba
Yaqinda men bir oz tajriba qildim va tuzoqlardan foydalanmasdan to'g'ri xatoliklarni osonlik bilan bajarishga imkon beruvchi || xatti-harakatini to'g'rilashning qulay usulini topdim. Mening javobim bo'limiga qarang. Ushbu usul haqida nima deb o'ylaysiz?
qo'shib qo'ydi muallif Buffer Over Read, manba
Ehtimol, faqat foyda komposabilitedir, chunki siz ishlashni boshlashdan avval o'rnatilgan boshqa tuzoqqa yozish uchun xavf tug'dirmaysiz. Keyinchalik manba va boshqa ssenariylardan foydalanishingiz mumkin bo'lgan ba'zi umumiy funktsiyalarni yozganda foydali xususiyatdir. Yana bir foyda to'liq POSIX muvofiqligi bo'lishi mumkin, garchi ERR kabi barcha muhim kabuklarda pseudo-signal qo'llab-quvvatlanadigan darajada muhim emas. Tadqiq uchun rahmat! =)
qo'shib qo'ydi muallif Buffer Over Read, manba
Taklif uchun tashakkur, men uni ta'qib qilishga harakat qilaman. Code Review haqida bilmagan.
qo'shib qo'ydi muallif Buffer Over Read, manba

Sizning skriptingiz kataloglarni o'zgartiradi, ya'ni ishlamaydi bir qator nisbatan yo'llar bilan. Keyin siz keyinchalik izohladingiz faqat foydalanish uchun qobiliyat emas, balki katalog mavjudligini tekshirishni xohlagan cd , shuning uchun javoblarga cd hech foydalanish shart emas. Qayta ko'rib chiqildi. tput dan foydalanish va man terminfo dan ranglar:

#!/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
}

(Muammoli emas, balki ko'proq invulnerable printf dan foydalanish uchun tahrirlangan echo matnida qochish suhbatida harakat qilishi mumkin.)

4
qo'shib qo'ydi
Bundan tashqari, (agar xpg_echo yoqilmagan bo'lsa) fayl nomlari teskari egri harflarni o'z ichiga olgan muammolarni ham hal qiladi.
qo'shib qo'ydi muallif Stéphane Chazelas, manba

@Gilles »javobini kengaytirish uchun:

Haqiqatan ham, set -e agar siz ularni pastki kassada ishga tushirsangiz ham || operatoridan keyin foydalanadigan buyruqlarda ishlamaydi; Masalan, bu ishlamaydi:

#!/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

Ammo, || operatori tashqi funksiyadan tozalashdan oldin qaytmasligini oldini olish uchun kerak.

Buni tuzatish uchun ishlatilishi mumkin bo'lgan kichik hiylalar mavjud: ichki buyruqni fonda ishga tushiring va darhol kuting. wait o'rnatilgan ichki buyruqning chiqish kodini qaytaradi va endi wait dan keyin wait dan foydalanib, ichki funktsiyani emas, shuning uchun set -e ishining oxirida to'g'ri ishlaydi:

#!/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

Bu fikrga asoslangan umumiy funktsiya. local kalit so'zlarni olib tashlasangiz, ya'ni x = y bilan barcha local x = y so'zini butunlay o'zgartirsangiz barcha POSIX mos keluvchi qobiqlarda ishlashi kerak:

# [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" "[email protected]" &

  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" "[email protected]"
    return $?
  fi

  return $exit_code
}


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

Foydalanish namunasi:

#!/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: [email protected]"
  CLEANUP=cleanup run inner "[email protected]"
  echo "<-- main"
}


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


cleanup() {
  echo "--> cleanup: [email protected]"
  echo "    RUN_CMD = '$RUN_CMD'"
  echo "    RUN_EXIT_CODE = $RUN_EXIT_CODE"
  sleep 0.3
  echo '<-- cleanup'
  return $RUN_EXIT_CODE
}

main "[email protected]"

Misolni ishga tushirish:

$ ./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

Ushbu usuldan foydalanganda siz xabardor bo'lishingiz kerak bo'lgan yagona narsa - bu sizning kodingiz run ga o'tib ketgan buyruqdan bajarilgan Shell o'zgaruvchilarining barcha modifikatsiyalari chaqiruv funktsiyasiga propagatsiya qilmaydi, chunki buyruq subshell.

3
qo'shib qo'ydi

catch --- hisoboti orqali nimani anglatishini ayta olmaysiz va davom eting; undan keyin ishlashni bekor qilish kerakmi?

cd xatolikka nol bo'lmagan holatni qaytarsa, siz shunday qilishingiz mumkin:

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

Omadsizlikdan chiqishingiz mumkin:

cd -- "$1" || exit 1

Yoki o'z xabaringizni aks ettirib chiqing:

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

cd tomonidan taqdim etilgan xatolikni bartaraf etish va/yoki uni bostirish:

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

By standards, commands should put error messages on STDERR (file descriptor 2). Thus 2>/dev/null says redirect STDERR to the "bit-bucket" known by /dev/null.

(o'zgaruvchilaringizni taklif qilishni unutmang va cd uchun variantlar sonini belgilang).

0
qo'shib qo'ydi
@Stephane Chazelas-ning quydagini keltirib chiqishi va uzatish variantlarini yaxshi qabul qilish nuqtasi. Tahrirlash uchun rahmat.
qo'shib qo'ydi muallif Mikkel, manba

Aslida sizning ishingiz uchun mantiqni yaxshilash mumkinligini aytaman.

CD ni emas, balki mavjudligini tekshiring, u mavjudligini tekshiring va katalogga o'ting.

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

But if your purpose is to silence the possible errors then cd -- "$1" 2>/dev/null, but this will make you debug in the future harder. You can check the if testing flags at: Bash if documentation:

0
qo'shib qo'ydi
Men aslida ma'lum bir katalog mavjudligini tekshirishga harakat qilardim. Lekin men yaxshi bilmaganligim uchun, CDni ishlatishga harakat qilaman deb o'yladim, agar bo'lmasa xato bo'lishi mumkin edi, shuning uchun uni nima uchun qo'lga olish kerak? Men [-d $ 1] kerakli narsani aniq bilmadim. Shunday qilib, sizga katta rahmat! (Java dasturini ishlatish uchun foydalandim va agar if statementda katalogni tekshirish Java-da keng tarqalmagan bo'lsa)
qo'shib qo'ydi muallif alexandroid, manba
Bu javob $ 1 o'zgaruvchiga murojaat qilishni amalga oshirmaydi va agar bu o'zgaruvchi blankalar yoki boshqa qobiqli metakarakteriyalar mavjud bo'lsa muvaffaqiyatsiz bo'ladi. Bundan tashqari, foydalanuvchining cd ga ruxsat bor-yo'qligini tekshirib bo'lmaydi.
qo'shib qo'ydi muallif Ian D. Allen, manba