Kuinka saada kiinni virheestä Linux bash -komentosarjassa?

Tein seuraavan komentosarjan:

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

Skripti toimii, mutta minun vieressä kaiku, siellä on myös lähtö, kun

cd $1 

epäonnistuu suorituksessa.

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

Onko tämä mahdollista kiinni?

Kommentit

  • Vain FYI, voit tehdä sen myös paljon yksinkertaisemmin; test -d /path/to/directory (tai bashissa [[ -d /path/to/directory ]]) kertoo, onko tietty kohde hakemisto vai ei, ja se tekee sen hiljaa.
  • @Patrick, joka vain testaa, onko se ’ sa hakemisto, ei, jos voit cd siihen.
  • @StephaneChazelas kyllä. Funktion nimi on directoryExists.
  • Katso yksityiskohtainen vastaus täältä: Korota virhe Bash-komentosarjassa .

Vastaa

Komentosarjasi muuttaa hakemistoja suorituksen aikana, mikä tarkoittaa, että se ei toimi sarja suhteellisia polkuja. Kommentoit myöhemmin, että halusit tarkistaa vain hakemiston olemassaolon, etkä kykyä käyttää cd, joten vastausten ei tarvitse käyttää cd ollenkaan. Tarkistettu. Käyttämällä tput ja värejä kohteesta 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 } 

(muokattu käyttää haavoittumattomampaa printf ongelmallisen echo sijasta, joka saattaa vaikuttaa tekstin pakosekvensseihin.)

Kommentit

  • Se korjaa myös ongelmat (ellei xpg_echo ole päällä), kun tiedostonimet sisältävät taaksepäin viivamerkkejä.

Vastaa

Määritä set -e -asetuksella exit-on-error-tila: jos yksinkertainen komento palauttaa nollasta poikkeavan tilan (mikä tarkoittaa vikaa), kuori poistuu.

Varo, että set -e ei aina potkaise sisään. Testiasennoissa olevien komentojen annetaan epäonnistua (esim. if failing_command, failing_command || fallback). Alikuoren komennot johtavat vain alikuoresta poistumiseen, ei vanhempaan: set -e; (false); echo foo näyttää foo.

Vaihtoehtoisesti tai lisäksi bash (ja ksh ja zsh, mutta ei tavallinen sh), voit määrittää komennon, joka suoritetaan, jos komento palauttaa nollatilan, ERR -loukulla, esim. trap "err=$?; echo >&2 "Exiting on error $err"; exit $err" ERR. Huomaa, että sellaisissa tapauksissa kuin (false); …, ERR-ansa suoritetaan alikuoressa, joten se ei voi aiheuttaa vanhemman poistumista.

Kommentit

  • Kokeilin äskettäin vähän ja löysin kätevän tavan korjata || -käyttäytyminen, jonka avulla virheiden oikea käsittely voidaan tehdä helposti ilman ansoja. vastaukseni . Mitä mieltä olet menetelmästä?
  • @ sam.kozin En ’ t: lla on aikaa tarkistaa vastauksesi yksityiskohtaisesti, se näyttää periaatteessa hyvältä. Mitkä ovat kannettavuuden lisäksi ksh / bash / zsh: n edut ’ s ERR-ansaan?
  • Ainoa hyöty on luultavasti yhdistettävyys, koska et ’ vaaraa korvata toisen ansan, joka oli asetettu ennen toiminnon suorittamista. Mikä on hyödyllinen ominaisuus, kun ’ kirjoittaa jonkin yleisen toiminnon, jonka hankit myöhemmin ja jota käytät muista skripteistä. Toinen etu saattaa olla täydellinen POSIX-yhteensopivuus, vaikka se ei olekaan niin tärkeää, koska ERR -pseudosignaalia tuetaan kaikissa pääkuorissa. Kiitos arvostelusta! =)
  • @ sam.kozin Unohdin kirjoittaa edelliseen kommenttiin: voit halutessasi lähettää tämän Code Review -palveluun ja lähettää linkki keskusteluhuoneessa .
  • Kiitos ehdotuksesta, yritän seurata ’ se. En tiennyt ’ tienne Code Review -sovelluksesta.

Vastaa

@Gilles ”-vastauksen laajentaminen:

Itse asiassa set -e ei toimi sisällä -komennot, jos käytät niiden jälkeen || -operaattoria, vaikka suoritatkin ne alikuoressa; esim. tämä ei toimi:

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

Mutta || -operaattoria tarvitaan estämään paluu ulkoisesta toiminnosta ennen puhdistusta.

Tämän korjaamiseen voidaan käyttää pientä temppua: Suorita sisempi komento taustalla ja sitten heti odota sitä.Sisäänrakennettu wait palauttaa sisäisen komennon poistumiskoodin, ja nyt käytät || -kuvaketta , ei sisäistä toimintoa, joten set -e toimii oikein jälkimmäisen sisällä:

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

Tässä on yleinen toiminto, joka perustuu tähän ajatukseen. Sen pitäisi toimia kaikissa POSIX-yhteensopivissa kuoreissa, jos poistat local avainsanat, ts. korvaa kaikki local x=y vain 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 }  

Esimerkki käytöstä:

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

Esimerkin suorittaminen:

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

Ainoa asia, jonka sinun on oltava tietoinen tätä menetelmää käytettäessä, on, että kaikki Shell-muuttujien muutokset tehdään com: lta run välitettävä mand ei siirry kutsutoimintoon, koska komento suoritetaan alikuoressa.

Vastaa

Et sano, mitä tarkoita catch — -raportilla ja jatka; keskeytetäänkö jatkokäsittely?

Koska cd palauttaa nollasta poikkeavan tilan epäonnistumisen yhteydessä, voit tehdä seuraavaa:

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

Voit yksinkertaisesti poistua epäonnistumisesta:

cd -- "$1" || exit 1 

Tai toista oma viesti ja lopeta:

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

Ja / tai estä cd -virheen aiheuttama virhe:

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

Standardien mukaan komentojen tulisi laittaa virhesanomat STDERR: ään (tiedostokuvaaja 2). Siten 2>/dev/null sanoo ohjaavan STDERR: n ”bit-bucketiin”, jonka /dev/null tietää.

(älä unohda lainata muuttujiasi ja merkitä vaihtoehtojen loppu osioon cd).

Kommentit

  • @Stephane Chazelas-piste lainaa ja merkitse vaihtoehtojen loppu hyvin. Kiitos muokkauksesta.

Vastaa

Oikeastaan sinun tapauksessasi sanoisin, että logiikkaa voidaan parantaa.

CD: n sijasta ja tarkista sitten, onko sitä olemassa, tarkista onko se olemassa, siirry sitten hakemistoon.

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

Mutta jos tarkoituksena on hiljentää mahdolliset virheet, cd -- "$1" 2>/dev/null, mutta tämä tekee sinusta jatkossa virheenkorjauksen vaikeammaksi. Voit tarkistaa if-testauksen liput osoitteessa: Bash jos dokumentaatio :

Kommentit

  • Tämä vastaus ei onnistu lainaa muuttujaa $1 ja epäonnistuu, jos muuttuja sisältää aihioita tai muita kuoren metahahmoja. Se ei myöskään tarkista, onko käyttäjällä lupa cd siihen.
  • Yritin itse asiassa tarkistaa, onko olemassa tietty hakemisto, ei välttämättä cd siihen . Mutta koska en tiennyt ’ en tiennyt paremmin, ajattelin, että CD-levylle yrittäminen aiheuttaisi virheen, jos sitä ei olisi, joten miksi et saisi sitä kiinni? En tiennyt ’ tiedä if [-d $ 1] että ’ tarkalleen mitä tarvitsin. Joten, kiitos paljon! (I ’ m käytin Java-ohjelmointia ja hakemiston tarkistamista if-lauseessa ei ole aivan yleistä Java-ohjelmassa)

Vastaa

Sähköpostiosoitettasi ei julkaista. Pakolliset kentät on merkitty *