Hogyan lehet hibát elkapni egy linux bash szkriptben?

A következő szkriptet készítettem:

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

A szkript működik, de a visszhangzik, ott van a kimenet is, amikor

cd $1 

a végrehajtás során nem sikerül.

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

Lehetséges ezt elkapni?

Megjegyzések

  • Csak egy FYI, ezt sokkal egyszerűbben is megteheti; A test -d /path/to/directory (vagy a bash [[ -d /path/to/directory ]]) megmondja, hogy egy adott célkönyvtár-e vagy sem, és ezt csendesen meg fogja csinálni. / li>
  • @Patrick, amely csak azt teszteli, hogy ‘ egy könyvtárat tartalmaz-e, és nem, ha cd be tudja-e venni.
  • @StephaneChazelas igen. A függvény neve directoryExists.
  • Itt olvashat részletes választ: Hiba emelése egy Bash szkriptben .

Válasz

A szkript futtatásakor megváltoztatja a könyvtárakat, ami azt jelenti, hogy nem fog működni relatív útvonalnevek sora. Ezután később megjegyezte, hogy csak a könyvtár létezését szeretné ellenőrizni, nem pedig a cd használatának lehetőségét, ezért a válaszoknak nem kell használni a cd egyáltalán. Felülvizsgált. A tput és a man terminfo színek felhasználásával:

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

(Szerkesztve hogy a sebezhetetlenebb printf -t használja a problematikus echo helyett, amely a menekülési szekvenciákra hathat a szövegben.)

Megjegyzések

  • Ez megoldja (hacsak az xpg_echo nincs bekapcsolva) azokat a problémákat is, amikor a fájlnevek visszavágó karaktereket tartalmaznak.

Válasz

A set -e paranccsal állítsa be a kilépés-hiba módot: ha egy egyszerű parancs nem nulla állapotot ad vissza (hibát jelez), a shell kilép.

Vigyázzon, hogy a set -e ne mindig rúgjon be. A tesztpozícióban lévő parancsok meghiúsulhatnak (pl. if failing_command, failing_command || fallback). Az alhéjban lévő parancsok csak az alhéjból való kilépéshez vezetnek, a szülő nem: set -e; (false); echo foo megjeleníti a foo.

Alternatív megoldásként vagy emellett a bash (és ksh és zsh, de nem sima sh), megadhat egy parancsot, amelyet “hajt végre, ha egy parancs nem nulla állapotot ad vissza, a ERR csapdával, pl. trap "err=$?; echo >&2 "Exiting on error $err"; exit $err" ERR. Vegye figyelembe, hogy olyan esetekben, mint a (false); …, az ERR csapda az alhéjban kerül végrehajtásra, így “nem okozhatja a szülő kilépését.

Megjegyzések

  • Nemrégiben kísérleteztem egy kicsit, és felfedeztem a || viselkedés kijavításának kényelmes módját, amely lehetővé teszi a hibakezelés egyszerű elvégzését csapdák használata nélkül. Lásd: válaszom . Mit gondolsz erről a módszerről?
  • @ sam.kozin Nem ‘ nek nincs ideje részletesen áttekinteni a választ, elvileg jól néz ki. A hordozhatóságon kívül milyen előnyökkel jár a ksh / bash / zsh ‘ s ERR csapda?
  • Valószínűleg az egyetlen előny az összeilleszthetőség, mivel nem ‘ nem kockáztatja, hogy felülírja egy másik csapdát, amelyet a funkció futtatása előtt állítottak be. id = “d72b74b94c”>

ír egy olyan általános funkciót, amelyet később más szkriptekből fog beszerezni és használni. További előny lehet, hogy teljes a POSIX kompatibilitás, bár ez nem olyan fontos, mivel azERRáljelet támogatják az összes főbb héjban. Köszönöm a véleményt! =)

  • @ sam.kozin Elfelejtettem írni az előző megjegyzésembe: érdemes ezt felkerülnie a Code Review oldalra, és link a csevegőszobában .
  • Köszönöm a javaslatot, megpróbálom követni ‘ azt. Nem tudtam ‘ a Code Review-ról.
  • Válasz

    A @Gilles “válasz kibővítése:

    Valójában a set -e nem működik belső parancsok, ha || operátort használsz utánuk, még akkor is, ha alhéjban futtatod őket; pl. ez nem működne:

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

    De || operátorra van szükség, hogy megakadályozzuk a visszatérést a külső függvényről a tisztítás előtt.

    Van egy kis trükk, amellyel ezt kijavíthatjuk: futtassuk a belső parancsot a háttérben, majd azonnal várni rá.A beépített wait visszaadja a belső parancs kilépési kódját, és most iv id = “8a710934b9” után a || szót használja. >

    , nem pedig a belső funkció, tehát aset -emegfelelően működik az utóbbin belül:

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

    Itt van egy általános funkció, amely erre az ötletre épít. Minden POSIX-kompatibilis héjban működnie kell, ha eltávolítja a local kulcsszavakat, vagyis cserélje le az összes local x=y szót csak 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 }  

    Példa használatra:

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

    A példa futtatása:

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

    A A módszer használata során csak annyit kell tudnia, hogy a Shell-változók minden módosítását a com-ról hajtják végre A run által átadott mand nem fog tovább terjedni a hívó függvényre, mert a parancs alhéjban fut.

    Válasz

    Nem mondja el, hogy mit ért pontosan a catch — jelentés alatt, és folytassa; megszakítja a további feldolgozást?

    Mivel a cd nem null állapotot ad vissza hiba esetén, ezt megteheti:

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

    Hiba esetén egyszerűen kiléphet:

    cd -- "$1" || exit 1 

    Vagy visszhangozhatja saját üzenetét és lépjen ki:

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

    És / vagy elnyomja a cd hibát:

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

    A szabványok szerint a parancsoknak hibaüzeneteket kell elhelyezniük az STDERR-en (2. fájlleíró). Így 2>/dev/null mondja az STDERR átirányítását a /dev/null által ismert “bit-vödörbe”.

    (ne felejtsd el a változók idézése és a cd) opciók végének megjelölése.

    Megjegyzések

    • @Stephane A Chazelas pontja a lehetőségek végének idézése és jelzése jól bevett. Köszönjük a szerkesztést.

    Válasz

    Valójában az Ön esetére azt mondanám, hogy a logika javítható.

    A CD helyett ellenőrizze, hogy létezik-e, ellenőrizze, hogy létezik-e, majd lépjen a könyvtárba.

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

    De ha célja a lehetséges hibák elhallgattatása, akkor cd -- "$1" 2>/dev/null, de ez a jövőben nehezebbé teszi a hibakeresést. Ellenőrizheti az if tesztelést megjelölés: Bash, ha dokumentáció :

    Megjegyzések

    • Ez a válasz nem sikerült idézd a $1 változót, és kudarcot vall, ha ez a változó üres vagy más shell metakaraktert tartalmaz. Azt sem ellenőrzi, hogy a felhasználónak van-e engedélye arra, hogy cd belépjen.
    • Valójában azt próbáltam ellenőrizni, hogy létezik-e egy bizonyos könyvtár, nem feltétlenül cd-vel. . De mivel nem tudtam ‘ nem tudtam jobban, azt gondoltam, hogy a CD-re való próbálkozás hibát okoz, ha nem létezik, akkor miért ne lehetne elkapni? Nem tudtam ‘ arról az if [-d $ 1] -ről, hogy ‘ pontosan az, amire szükségem van. Szóval, köszönöm szépen! (Én ‘ m használtam a Java programozásához, és ha egy utasításban könyvtárat keresek, ha az utasítás nem éppen gyakori a Java-ban)

    Vélemény, hozzászólás?

    Az email címet nem tesszük közzé. A kötelező mezőket * karakterrel jelöltük