Jag skapade följande manus:
# !/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"
Manuset fungerar, men bredvid min ekar, det finns också utdata när
cd $1
misslyckas vid körning.
testscripts//test_labo3: line 11: cd: ~/foobar: No such file or directory
Är det möjligt att fånga det här?
Kommentarer
Svar
Ditt skript ändrar kataloger när det körs, vilket betyder att det inte fungerar med en serie relativa sökvägar. Du kommenterade sedan senare att du bara ville kontrollera om katalogen fanns, inte förmågan att använda cd
, så svar behöver inte använda cd
alls. Reviderad. Med tput
och färger från 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 }
(redigerad att använda det mer osårbara printf
istället för det problematiska echo
som kan agera på escape-sekvenser i texten.)
Kommentarer
- Det löser också (om inte xpg_echo är på) problemen när filnamn innehåller bakåtvänd snedstreck.
Svar
Använd set -e
för att ställa in exit-on-error-läge: om ett enkelt kommando returnerar en status som inte är noll (indikerar misslyckande), skalet går ut.
Var uppmärksam på att set -e
inte alltid sparkar in. Kommandon i testpositioner får misslyckas (t.ex. if failing_command
, failing_command || fallback
). Kommandon i subshell leder bara till att subshell avslutas, inte den överordnade: set -e; (false); echo foo
visar foo
.
Alternativt eller dessutom i bash (och ksh och zsh, men inte vanligt sh), kan du ange ett kommando som utförs om ett kommando returnerar en status som noll, med ERR
fällan, t.ex. trap "err=$?; echo >&2 "Exiting on error $err"; exit $err" ERR
. Observera att i fall som (false); …
exekveras ERR-fällan i subshell, så det kan inte få föräldern att lämna.
Kommentarer
- Nyligen experimenterade jag lite och upptäckte ett bekvämt sätt att fixa
||
beteende, vilket gör det enkelt att göra korrekt felhantering utan att använda fällor. Se mitt svar . Vad tycker du om den metoden? - @ sam.kozin Jag don ’ t har tid att granska ditt svar i detalj, det ser bra ut i princip. Förutom portabilitet, vad är fördelarna med ksh / bash / zsh ’ s ERR trap?
- Förmodligen är den enda fördelen komposibilitet, eftersom du inte ’ t riskerar att skriva över en annan fälla som var inställd innan du körs. Vilket är en användbar funktion när du id = ”d72b74b94c”>
skriver en vanlig funktion som du senare kommer att källa och använda från andra skript. En annan fördel kan vara full POSIX-kompatibilitet, men det är inte så viktigt eftersomERR
pseudosignal stöds i alla större skal. Tack för recensionen! =)
Svar
För att utöka svaret på @Gilles :
Faktum är att set -e
inte fungerar inuti-kommandon om du använder ||
efter dem, även om du kör dem i en subshell; till exempel skulle detta inte fungera:
#!/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
Men ||
operatör behövs för att förhindra återkomst från den yttre funktionen innan rengöring.
Det finns ett litet trick som kan användas för att åtgärda detta: kör det inre kommandot i bakgrunden och sedan omedelbart vänta på det.Det inbyggda wait
returnerar utgångskoden för det inre kommandot och nu använder du ||
efter wait
, inte den inre funktionen, så set -e
fungerar ordentligt inuti den senare:
#!/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
Här är den generiska funktionen som bygger på denna idé. Den ska fungera i alla POSIX-kompatibla skal om du tar bort local
nyckelord, dvs ersätt alla local x=y
med bara 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 }
Exempel på användning:
#!/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 "$@"
Kör exemplet:
$ ./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
enda som du behöver vara medveten om när du använder den här metoden är att alla modifieringar av Shell-variabler gjorda från com mand du skickar till run
sprids inte till anropsfunktionen, eftersom kommandot körs i en subshell.
Svar
Du säger inte vad du menar exakt med catch
— rapportera och fortsätt; avbryta vidare bearbetning?
Eftersom cd
returnerar en status som inte är noll vid misslyckande kan du göra:
cd -- "$1" && echo OK || echo NOT_OK
Du kan helt enkelt avsluta vid misslyckande:
cd -- "$1" || exit 1
Eller echo ditt eget meddelande och avsluta:
cd -- "$1" || { echo NOT_OK; exit 1; }
Och / eller undertrycka felet från cd
vid fel:
cd -- "$1" 2>/dev/null || exit 1
Som standard ska kommandon placera felmeddelanden på STDERR (filbeskrivare 2). Således säger 2>/dev/null
omdirigera STDERR till ”bit-bucket” känd av /dev/null
.
(glöm inte för att citera dina variabler och markera slutet på alternativen för cd
).
Kommentarer
- @Stephane Chazelas poäng att citera och signalera slut-av-alternativ väl tagna. Tack för redigeringen.
Svar
Egentligen för ditt fall skulle jag säga att logiken kan förbättras.
Istället för cd och kontrollera om den finns, kontrollera om den finns och gå in i katalogen.
if [ -d "$1" ] then printf "${green}${NC}\\n" "$1" cd -- "$1" else printf "${red}${NC}\\n" "$1" fi
Men om ditt syfte är att tysta de möjliga felen så cd -- "$1" 2>/dev/null
, men detta kommer att göra dig felsökning i framtiden svårare. Du kan kontrollera om testning flaggor på: Bash om dokumentation :
Kommentarer
- Det här svaret misslyckas med att citera variabeln
$1
och misslyckas om den variabeln innehåller blanksteg eller andra skalmetatecken. Det går inte att kontrollera om användaren har behörighet attcd
in i den. - Jag försökte faktiskt kontrollera om en viss katalog fanns, inte nödvändigtvis cd till den . Men eftersom jag inte ’ inte visste bättre, tänkte jag att försöka cd till det skulle orsaka ett fel om det inte fanns så varför inte fånga det? Jag visste inte ’ om if [-d $ 1] som ’ är exakt vad jag behövde. Så tack så mycket! (Jag ’ används för att proramera Java och söka efter en katalog i ett if-uttalande är inte exakt vanligt i Java)
test -d /path/to/directory
(eller[[ -d /path/to/directory ]]
i bash) kommer att berätta om ett visst mål är en katalog eller inte, och det kommer att göra det tyst.cd
i den.directoryExists
.