Comment attraper une erreur dans un script bash linux?

Jai créé le script suivant:

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

Le script fonctionne, mais à côté de mon écho, il y a aussi la sortie quand

cd $1 

échoue à lexécution.

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

Est-il possible dattraper ça?

Commentaires

  • Juste un FYI, vous pouvez aussi le faire beaucoup plus simple; test -d /path/to/directory (ou [[ -d /path/to/directory ]] dans bash) vous dira si une cible donnée est un répertoire ou non, et il le fera tranquillement.
  • @Patrick, qui teste simplement sil ‘ sa répertoire, pas si vous pouvez cd dedans.
  • @StephaneChazelas oui. Le nom de la fonction est directoryExists.
  • Voir une réponse détaillée ici: Erreur de déclenchement dans un script Bash .

Réponse

Votre script change de répertoire au cours de son exécution, ce qui signifie quil ne fonctionnera pas avec une série de chemins daccès relatifs. Vous avez ensuite commenté plus tard que vous vouliez uniquement vérifier lexistence du répertoire, pas la possibilité dutiliser cd, donc les réponses nont pas besoin dutiliser cd du tout. Modifié. Utilisation de tput et des couleurs de 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 } 

(Modifié pour utiliser le plus invulnérable printf au lieu du problématique echo qui pourrait agir sur les séquences déchappement dans le texte.)

Commentaires

  • Cela corrige également (sauf si xpg_echo est activé) les problèmes lorsque les noms de fichiers contiennent des barres obliques inverses.

Réponse

Utilisez set -e pour définir le mode de sortie sur erreur: si une simple commande renvoie un état différent de zéro (indiquant un échec), le shell se ferme.

Attention, set -e ne se déclenche pas toujours. Les commandes dans les positions de test peuvent échouer (par exemple, if failing_command, failing_command || fallback). Les commandes du sous-shell conduisent uniquement à quitter le sous-shell, pas le parent: set -e; (false); echo foo affiche foo.

Alternativement, ou en plus, dans bash (et ksh et zsh, mais pas simplement sh), vous pouvez spécifier une commande qui « est exécutée au cas où une commande retourne un statut différent de zéro, avec le piège ERR, par exemple trap "err=$?; echo >&2 "Exiting on error $err"; exit $err" ERR. Notez que dans des cas comme (false); …, le trap ERR est exécuté dans le sous-shell, il ne peut donc « pas provoquer la fermeture du parent.

Commentaires

  • Récemment, jai un peu expérimenté et découvert un moyen pratique de corriger le comportement de ||, ce qui permet de gérer facilement les erreurs sans utiliser dinterruptions. Voir ma réponse . Que pensez-vous de cette méthode?
  • @ sam.kozin I don ‘ t avoir le temps de revoir votre réponse en détail, elle semble bonne en principe. Outre la portabilité, quels sont les avantages par rapport au piège ERR de ksh / bash / zsh ‘?
  • Le seul avantage est probablement la composabilité, car vous ne risquez ‘ décraser un autre trap qui a été défini avant lexécution de la fonction. Ce qui est utile lorsque vous ‘ réécriture dune fonction commune que vous utiliserez plus tard à partir dautres scripts. Autre avantage peut être une compatibilité POSIX complète, bien que ce ne soit pas aussi important que le pseudo-signal ERR est pris en charge dans tous les shells majeurs. Merci pour lexamen! =)
  • @ sam.kozin Jai oublié décrire dans mon commentaire précédent: vous pouvez publier ceci sur Révision de code et publier un lien dans le salon de discussion .
  • Merci pour la suggestion, je ‘ essayer de suivre il. Je ‘ ne connaissais pas la révision du code.

Réponse

Pour développer la réponse @Gilles « :

En effet, set -e ne fonctionne pas à lintérieur des commandes si vous utilisez lopérateur || après elles, même si vous les exécutez dans un sous-shell; par exemple, cela ne fonctionnerait pas:

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

Mais est nécessaire pour empêcher le retour de la fonction externe avant le nettoyage.

Il existe une petite astuce qui peut être utilisée pour résoudre ce problème: exécutez la commande interne en arrière-plan, puis immédiatement attendez-le.Le wait intégré renverra le code de sortie de la commande interne, et maintenant vous « utilisez || après wait, pas la fonction interne, donc set -e fonctionne correctement à lintérieur de cette dernière:

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

Voici la fonction générique qui sappuie sur cette idée. Elle devrait fonctionner dans tous les shells compatibles POSIX si vous supprimez local mots clés, cest-à-dire remplacer tous les local x=y par 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 }  

Exemple dutilisation:

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

Exécution de lexemple:

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

Le la seule chose dont vous devez être conscient lors de lutilisation de cette méthode est que toutes les modifications des variables Shell effectuées à partir du com mand que vous passez à run ne se propage pas à la fonction appelante, car la commande sexécute dans un sous-shell.

Réponse

Vous ne dites pas ce que vous entendez exactement par catch — rapportez et continuez; abandonner le traitement?

Puisque cd renvoie un statut différent de zéro en cas déchec, vous pouvez faire:

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

Vous pouvez simplement quitter en cas déchec:

cd -- "$1" || exit 1 

Ou, faites écho à votre propre message et quittez:

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

Et / ou supprimer lerreur fournie par cd en cas déchec:

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

Par standard, les commandes doivent mettre des messages derreur sur STDERR (descripteur de fichier 2). Ainsi 2>/dev/null dit rediriger STDERR vers le « bit-bucket » connu par /dev/null.

(ne pas oublier pour citer vos variables et marquer la fin des options pour cd).

Commentaires

  • @Stephane Chazelas point de citation et de signalisation de fin doptions bien pris. Merci davoir édité.

Réponse

En fait pour votre cas, je dirais que la logique peut être améliorée.

Au lieu de cd et puis vérifiez sil existe, vérifiez sil existe puis allez dans le répertoire.

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

Mais si votre objectif est de réduire au silence les erreurs possibles, cd -- "$1" 2>/dev/null, mais cela vous rendra plus difficile le débogage à lavenir. Vous pouvez vérifier le test if flags at: Bash if documentation :

Commentaires

  • Cette réponse échoue citer la variable $1 et échouera si cette variable contient des blancs ou dautres métacaractères shell. Il ne parvient pas non plus à vérifier si lutilisateur a la permission dy cd.
  • Jessayais en fait de vérifier si un certain répertoire existait, pas nécessairement dy accéder . Mais comme je ne savais pas mieux ‘, je pensais quessayer dy accéder causerait une erreur sil nexistait pas alors pourquoi ne pas lattraper? Je ne savais ‘ que si [-d $ 1] que ‘ est exactement ce dont javais besoin. Alors merci beaucoup! (Je ‘ m utilisé pour proram Java, et la recherche dun répertoire dans une instruction if nest pas exactement courante en Java)

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *