Comment écrire un script Bash robuste (partage d'expérience)

2020-05-22

Le script shell sera grandement affecté lorsqu'il s'exécutera anormalement.

Cet article présente certaines techniques qui rendent les scripts bash robustes.

Utilisez set -u

Combien de fois le script s'est-il bloqué parce que les variables n'ont pas été initialisées? Pour moi, plusieurs fois.
chroot = $ 1
...
rm -rf $ chroot / usr / share / doc
Si le code ci-dessus est exécuté sans paramètres, il ne supprimera pas simplement les documents dans chroot, et Tous les documents du système sont supprimés. Qu'est-ce qui devrait être fait? Heureusement, bash fournit set -u, lorsque vous utilisez des variables non initialisées, laissez bash quitter automatiquement.

Vous pouvez également utiliser set-o nounset, qui est plus lisible.


copiez le code le code est le suivant:


david% bash /tmp/shrink-chroot.sh
$ chroot =
david% bash -u /tmp/shrink-chroot.sh
/tmp/shrink-chroot.sh: ligne 3: $ 1: variable non liée
david%

Utilisez set -e

Le début de chaque script écrit doit inclure set -e. Cela indique à bash de quitter bash une fois qu'une instruction renvoie une valeur non vraie. L'avantage d'utiliser -e est d'éviter l'erreur de faire boule de neige en erreurs graves et de rattraper les erreurs le plus tôt possible. Une version plus lisible: set -o errexit

Utilisez -e pour vous libérer de la vérification des erreurs. Si vous oubliez de vérifier, bash le fera pour vous. Mais il n'y a aucun moyen d'utiliser $? Pour obtenir l'état d'exécution de la commande, car bash ne peut pas obtenir de valeur de retour non nulle. Une autre structure peut être utilisée:

commande

if ["$?" - Ne 0]; then echo "command failed"; exit 1; fi

peut être remplacé par:

commande || {écho "échec de la commande"; sortie 1;}

Ou utilisez:

if! Command; then echo "command failed"; exit 1; fi

Que faire si vous devez utiliser une commande qui renvoie une valeur non nulle ou si la valeur de retour ne vous intéresse pas? Vous pouvez utiliser la commande || true, ou il existe un code long qui peut désactiver temporairement la vérification des erreurs, mais je recommande de l'utiliser avec prudence.

ensemble + e

commande1

commande2

set -e

Les documents associés indiquent que bash renvoie la valeur de la dernière commande du pipeline par défaut, peut-être celle que vous ne voulez pas. Par exemple, l'exécution de false | true sera considérée comme une exécution réussie de la commande. Si vous voulez qu'une telle commande soit considérée comme un échec, vous pouvez utiliser set -o pipefail

Défense procédurale - tenez compte des choses inattendues Le script de

peut être exécuté sous un compte "accidentel", tel que des fichiers manquants ou des répertoires non créés. Vous pouvez faire quelque chose pour éviter ces erreurs. Par exemple, après avoir créé un répertoire, si le répertoire parent n'existe pas, la commande mkdir renverra une erreur. Si vous ajoutez l'option -p à la commande mkdir lors de la création d'un répertoire, il créera le répertoire parent requis avant de créer le répertoire requis. Un autre exemple est la commande rm. Si vous souhaitez supprimer un fichier qui n'existe pas, il "crachera" et le script cessera de fonctionner. (Parce que l'option -e est utilisée, n'est-ce pas?) Vous pouvez utiliser l'option -f pour résoudre ce problème et laisser le script continuer à fonctionner lorsque le fichier n'existe pas.

Prêt à traiter les espaces dans les noms de fichiers

Certaines personnes utilisent des espaces dans les noms de fichiers ou les paramètres de ligne de commande, et doivent s'en souvenir lors de l'écriture de scripts. N'oubliez pas de toujours entourer les variables de guillemets.

si [$ filename = "foo"];

Lorsque la variable $ filename contient des espaces, elle raccroche. Il peut être résolu comme ceci:

if ["$ filename" = "foo"];

Lorsque vous utilisez la variable $ @, vous devez également utiliser des guillemets, car les deux paramètres séparés par des espaces seront interprétés comme deux parties distinctes.


copiez le code le code est le suivant:


david% foo () {for i in $ @; do echo $ i ; done}; foo bar "baz quux"
bar
baz
quux
david% foo () {for i in "$ @"; do echo $ i; done}; foo bar " baz quux "
bar
baz quux

Je n'ai pensé à aucun moment où "$ @" ne peut pas être utilisé, donc en cas de doute, il n'y a pas d'erreur dans l'utilisation des guillemets.

Si vous utilisez find et xargs en même temps, vous devez utiliser -print0 pour diviser les caractères dans les noms de fichiers au lieu des sauts de ligne.


copiez le code le code est le suivant:


david% touch "foo bar"
david% find | xargs ls
ls: ./foo: aucun fichier ou répertoire de ce type
ls: bar: aucun fichier ou répertoire de ce type
david% find -print0 | xargs -0 ls
./foo bar
< br>

Définir les pièges

Lorsque le script écrit raccroche, le système de fichiers est dans un état inconnu. Par exemple, verrouillez l'état du fichier, l'état du fichier temporaire ou raccrochez après avoir mis à jour un fichier avant de mettre à jour le fichier suivant. Si vous pouvez résoudre ces problèmes, que ce soit pour supprimer le fichier de verrouillage ou revenir à un état connu lorsque le script rencontre des problèmes, c'est très bien. Heureusement, bash fournit un moyen d'exécuter une commande ou une fonction lorsque bash reçoit un signal UNIX. Vous pouvez utiliser la commande trap.

signal de commande de déroutement [signal ...]

peut lier plusieurs signaux (la liste peut être obtenue en utilisant kill -l), mais pour nettoyer la fin de partie, nous n'en utilisons que trois: INT, TERM et EXIT. Vous pouvez utiliser -as pour restaurer les pièges à leur état initial.

Description du signal
INT
Déclenché par interruption lorsque quelqu'un utilise Ctrl-C pour terminer le script

TERM
Terminate-triggered when some use kill to kill the script process

EXIT
Exit-Ceci est un pseudo signal, qui est déclenché lorsque le script se termine normalement ou après que set -e se termine en raison d'une erreur

Lorsque vous utilisez un fichier de verrouillage, vous pouvez écrire comme ceci:


copiez le code le code est le suivant:


if; then
touchez $ lockfile [-e $ lockfile!]
section critique
rm $ lockfile
sinon
echo "la section critique est déjà en cours d'exécution"
fi

<

www.xd1998.com@2001-2030Partage De Technologie