Je transmets la réponse de Norman à 6 lignes, et le dernier de ceux-ci est vide:
#!/bin/ksh
#
# @(#)$Id$
#
# Purpose
La troisième ligne est une chaîne d'identification de contrôle de version - il est en fait un hybride avec un CSSC marqueur '@(#)
' qui peut être identifié par le programme (SCCS) what
et une chaîne de version RCS qui est développée lorsque le fichier est placé sous RCS, le VCS par défaut que j'utilise pour mon usage privé. Le programme RCS ident
récupère la forme développée de $Id$
, ce qui peut ressembler à $Id: mkscript.sh,v 2.3 2005/05/20 21:06:35 jleffler Exp $
. La cinquième ligne me rappelle que le script devrait avoir une description de son but en haut; Je remplace le mot par une description réelle du script (ce qui explique pourquoi il n'y a pas de deux-points après celui-ci, par exemple). Par la suite, il n'y a pratiquement rien de standard pour un script shell. Des fragments standard apparaissent, mais aucun fragment standard n'apparaît dans chaque script. (Ma discussion suppose que les scripts sont écrits dans des notations shell Bourne, Korn, ou POSIX (Bash) Il y a une discussion séparée sur la raison pour laquelle quelqu'un mettant un dérivé C Shell après le sigil #!
vit dans le péché.)
Par exemple, ce code apparaît sous une forme ou sous forme chaque fois qu'un script crée des fichiers intermédiaires (temporaires):
tmp=${TMPDIR:-/tmp}/prog.$$
trap "rm -f $tmp.?; exit 1" 0 1 2 3 13 15
...real work that creates temp files $tmp.1, $tmp.2, ...
rm -f $tmp.?
trap 0
exit 0
La première ligne choisit un répertoire temporaire, par défaut/tmp si l'utilisateur n'a pas spécifiez une alternative ($ TMPDIR est très largement reconnu et est standardisé par POSIX). Il crée ensuite un préfixe de nom de fichier incluant l'ID de processus. Ce n'est pas une mesure de sécurité; il s'agit d'une mesure de simultanéité simple, empêchant plusieurs instances du script de piétiner les données de l'autre. (Pour des raisons de sécurité, utilisez des noms de fichiers non prévisibles dans un répertoire non public.) La deuxième ligne garantit que les commandes 'rm
' et 'exit
' sont exécutées si le shell reçoit l'un des signaux SIGHUP (1), SIGINT (2), SIGQUIT (3), SIGPIPE (13) ou SIGTERM (15). La commande 'rm
' supprime tous les fichiers intermédiaires correspondant au modèle; la commande exit
garantit que l'état est différent de zéro, indiquant une sorte d'erreur. Le 'trap
' de 0 signifie que le code est également exécuté si le shell se termine pour une raison quelconque - il couvre la négligence dans la section «travail réel». Le code à la fin supprime alors tous les fichiers temporaires survivants, avant en levant le piège à la sortie, et enfin quitte avec un statut zéro (succès). De toute évidence, si vous voulez quitter avec un autre statut, vous pouvez - assurez-vous de le définir dans une variable avant d'exécuter les lignes rm
et trap
, puis d'utiliser exit $exitval
.
J'utilise habituellement ce qui suit pour supprimer le chemin et le suffixe du script, donc je peux utiliser $arg0
quand signaler les erreurs:
arg0=$(basename $0 .sh)
J'utilise souvent une fonction shell pour signaler les erreurs:
error()
{
echo "$arg0: $*" 1>&2
exit 1
}
S'il n'y a qu'une ou peut-être deux sorties d'erreur, je ne dérange pas avec la fonction; s'il y en a d'autres, je le fais car cela simplifie le codage. Je crée également des fonctions plus ou moins élaborées appelées usage
pour donner le résumé de l'utilisation de la commande - encore une fois, seulement s'il y a plus d'un endroit où il serait utilisé.
Un autre fragment assez standard est une boucle d'analyse syntaxique de l'option, en utilisant le haut-shell getopts
:
vflag=0
out=
file=
Dflag=
while getopts hvVf:o:D: flag
do
case "$flag" in
(h) help; exit 0;;
(V) echo "$arg0: version $Revision$ ($Date$)"; exit 0;;
(v) vflag=1;;
(f) file="$OPTARG";;
(o) out="$OPTARG";;
(D) Dflag="$Dflag $OPTARG";;
(*) usage;;
esac
done
shift $(expr $OPTIND - 1)
ou:
shift $(($OPTIND - 1))
Les guillemets autour des espaces de poignée "OPTARG $" dans les arguments. Le Dflag est cumulatif, mais la notation utilisée ici perd la trace des espaces dans les arguments. Il existe des moyens (non standard) de contourner ce problème. La première notation de décalage fonctionne avec n'importe quel shell (ou ferait si j'utilisais back-ticks au lieu de '$(...)
' .Le second fonctionne dans les coquilles modernes, il pourrait même y avoir une alternative entre crochets au lieu de parenthèses, mais cette Un dernier truc pour l'instant est que j'ai souvent à la fois la version GNU et une version non-GNU des programmes, et je veux être en mesure de choisir J'utilise.Beaucoup de mes scripts, donc utiliser des variables telles que:
: ${PERL:=perl}
: ${SED:=sed}
Et puis, quand je dois appeler Perl ou sed
, le script utilise $PERL
ou $SED
. Cela m'aide quand quelque chose se comporte différemment - je peux choisir la version opérationnelle - ou pendant le développement du script (je peux ajouter des options de débogage uniquement à la commande sans modifier le script).
Auteur (s)? Changelog? Obtenez un système de contrôle de version! – derobert