Comment puis-je ajouter une méthode d'aide à un script shell?

128

Comment vérifier si un -hattribut a été passé dans un script shell? Je souhaite afficher un message d'aide lorsqu'un utilisateur appelle myscript.sh -h.

tttppp
la source
Exemples pertinents: stackoverflow.com/questions/14008125/…
Anton Tarasenko

Réponses:

173

voici un exemple pour bash:

usage="$(basename "$0") [-h] [-s n] -- program to calculate the answer to life, the universe and everything

where:
    -h  show this help text
    -s  set the seed value (default: 42)"

seed=42
while getopts ':hs:' option; do
  case "$option" in
    h) echo "$usage"
       exit
       ;;
    s) seed=$OPTARG
       ;;
    :) printf "missing argument for -%s\n" "$OPTARG" >&2
       echo "$usage" >&2
       exit 1
       ;;
   \?) printf "illegal option: -%s\n" "$OPTARG" >&2
       echo "$usage" >&2
       exit 1
       ;;
  esac
done
shift $((OPTIND - 1))

Pour utiliser ceci dans une fonction:

  • utiliser "$FUNCNAME"au lieu de$(basename "$0")
  • ajouter local OPTIND OPTARGavant d'appelergetopts
Glenn Jackman
la source
1
J'essaye ceci dans une fonction, mais quand j'essaye d'exécuter la fonction j'obtiens cette erreur "basename: option invalide - 'b'". On dirait qu'il essaie de passer "-bash" basenameavec le tiret de tête.
Morgan Estes
5
imside une fonction utiliser "$FUNCNAME"pas "$0". Aussi, ajouterlocal OPTIND OPTARG
glenn jackman
Merci. FUNCNAMEtravaux. J'ai toutes mes fonctions dans un seul fichier, c'est donc parfait pour les étendre en quelque chose d'utile pour les autres.
Morgan Estes
5
@sigur, assurez-vous de citer "$usage" chaque endroit où vous l'utilisez.
glenn jackman
1
Quel est le shift $((OPTIND - 1))pour?
hpaknia
45

Le premier argument d'un script shell est disponible comme variable $1, donc l'implémentation la plus simple serait

if [ "$1" == "-h" ]; then
  echo "Usage: `basename $0` [somestuff]"
  exit 0
fi

Mais ce qu'a dit anubhava.

Seb
la source
Merci @MarkBooth, faute de frappe corrigée (plus amélioration par mise entre guillemets)
seb
Vous devriez prendre l'habitude d'envelopper le if in [...] pour les conditionnels pour éviter une mauvaise analyse d'une variable, source: github.com/bahamas10/bash-style-guide#bashisms
JREAM
2
Oui, bien que l'OP n'ait pas spécifié bash et qu'il s'agisse de la [version compatible POSIX.
seb
Remarque - Pour une utilisation à l'intérieur function: vous devez remplacer exit 0par returnsi vous ne voulez pas terminer votre shell après avoir exécuté votre fonction. (Je l'ai fait avant 😂)
Illuminator
29

voici une partie que je l'utilise pour démarrer un serveur VNC

#!/bin/bash
start() {
echo "Starting vnc server with $resolution on Display $display"
#your execute command here mine is below
#vncserver :$display -geometry $resolution
}

stop() {
echo "Killing vncserver on display $display"
#vncserver -kill :$display
}

#########################
# The command line help #
#########################
display_help() {
    echo "Usage: $0 [option...] {start|stop|restart}" >&2
    echo
    echo "   -r, --resolution           run with the given resolution WxH"
    echo "   -d, --display              Set on which display to host on "
    echo
    # echo some stuff here for the -a or --add-options 
    exit 1
}

################################
# Check if parameters options  #
# are given on the commandline #
################################
while :
do
    case "$1" in
      -r | --resolution)
          if [ $# -ne 0 ]; then
            resolution="$2"   # You may want to check validity of $2
          fi
          shift 2
          ;;
      -h | --help)
          display_help  # Call your function
          exit 0
          ;;
      -d | --display)
          display="$2"
           shift 2
           ;;

      -a | --add-options)
          # do something here call function
          # and write it in your help function display_help()
           shift 2
           ;;

      --) # End of all options
          shift
          break
          ;;
      -*)
          echo "Error: Unknown option: $1" >&2
          ## or call function display_help
          exit 1 
          ;;
      *)  # No more options
          break
          ;;
    esac
done

###################### 
# Check if parameter #
# is set too execute #
######################
case "$1" in
  start)
    start # calling function start()
    ;;
  stop)
    stop # calling function stop()
    ;;
  restart)
    stop  # calling function stop()
    start # calling function start()
    ;;
  *)
#    echo "Usage: $0 {start|stop|restart}" >&2
     display_help

     exit 1
     ;;
esac

C'est un peu bizarre que j'ai placé le redémarrage start stop dans un cas séparé mais cela devrait fonctionner

Vincent Stans
la source
si vous donnez une option vide à -d; n'est-ce pas une boucle infinie?
zerobane le
Pourquoi quittez-vous 1 dans la fonction d'assistance?
jeantimex le
17

Pour une solution rapide à option unique, utilisez if

Si vous n'avez qu'une seule option à vérifier et que ce sera toujours la première option ( $1), alors la solution la plus simple est une ifavec un test ( [). Par exemple:

if [ "$1" == "-h" ] ; then
    echo "Usage: `basename $0` [-h]"
    exit 0
fi

Notez que pour posix, la compatibilité =fonctionnera aussi bien que ==.

Pourquoi citer $1?

La raison pour laquelle il $1faut mettre entre guillemets est que s'il n'y en a pas, $1le shell essaiera de s'exécuter if [ == "-h" ]et échouera car il ==n'a reçu qu'un seul argument lorsqu'il en attendait deux:

$ [ == "-h" ]
bash: [: ==: unary operator expected

Pour toute utilisation plus complexe getoptougetopts

Comme suggéré par d' autres , si vous avez plus d'une seule option simple, ou si vous avez besoin de votre option pour accepter un argument, vous devez absolument opter pour la complexité supplémentaire de l'utilisation getopts.

Comme référence rapide, j'aime le tutoriel getopts de 60 secondes .

Vous pouvez également envisager le getoptprogramme au lieu du shell intégré getopts. Il permet l'utilisation d'options longues et d'options après des arguments non optionnels (par exemple foo a b c --verboseplutôt que juste foo -v a b c). Cette réponse Stackoverflow explique comment utiliser GNU getopt.

Jeffbyrnes a mentionné que le lien original était mort mais heureusement, la machine de retour l' avait archivé.

Mark Booth
la source
Merci! J'utilise avec plaisir getopts depuis un an maintenant, mais je vais aussi jeter un œil à getopt.
tttppp
1
Malheureusement, le lien vers le didacticiel de 60 secondes getopts est mort; il semble que bashcurescancer.com n'existe plus. Voici un lien vers la version de Wayback Machine .
jeffbyrnes
-1

Je pense que vous pouvez utiliser le cas pour cela ...

case $1 in 
 -h) echo $usage ;; 
  h) echo $usage ;;
help) echo $usage ;;
esac
nouvel utilisateur
la source