¿Cómo puedo agregar un método de ayuda a un script de shell?

Respuestas:

173

Aquí hay un ejemplo para 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))

Para usar esto dentro de una función:

  • usar en "$FUNCNAME"lugar de$(basename "$0")
  • agregar local OPTIND OPTARGantes de llamargetopts
Glenn Jackman
fuente
1
Estoy intentando esto dentro de una función, pero cuando intento ejecutar la función me sale este error "basename: opción no válida - 'b'". Parece que está tratando de pasar "-bash" basenamecon el guión principal.
Morgan Estes
55
imponer una función de uso "$FUNCNAME"no "$0". Además, agreguelocal OPTIND OPTARG
glenn jackman el
Gracias. FUNCNAMEtrabajos. Tengo todas mis funciones dentro de un solo archivo, por lo que es perfecto para extenderlas en algo útil para otros.
Morgan Estes
55
@sigur, asegúrese de citar "$usage" cada lugar donde lo use.
Glenn Jackman
1
¿Para qué sirve shift $((OPTIND - 1))?
hpaknia
45

El primer argumento para un script de shell está disponible como la variable $1, por lo que la implementación más simple sería

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

Pero lo que dijo Anubhava.

seb
fuente
Gracias @MarkBooth, error tipográfico corregido (más mejoras al incluir comillas)
seb
Debes acostumbrarte a envolver el if [...] para los condicionales para evitar un mal análisis de una variable, fuente: github.com/bahamas10/bash-style-guide#bashisms
JREAM
2
Sí, aunque el OP no especificó bash, y [es la versión compatible con POSIX.
seb
Nota - Para usar dentro function: debe reemplazar exit 0con returnsi no desea terminar su shell después de ejecutar su función. (Lo he hecho antes 😂)
Illuminator
29

Aquí hay una parte que lo uso para iniciar un servidor 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

Es un poco extraño que coloque el inicio, parada, reinicio en un caso separado, pero debería funcionar

Vincent Stans
fuente
si le da una opción vacía a -d; ¿No será un bucle infinito?
zerobane el
¿Por qué sale 1 en la función auxiliar?
jeantimex
17

Para una solución rápida de opción única, use if

Si solo tiene una opción para verificar y siempre será la primera opción ( $1), entonces la solución más simple es ifcon una prueba ( [). Por ejemplo:

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

Tenga en cuenta que para la compatibilidad posix =funcionará tan bien como ==.

¿Por qué cotizar $1?

La razón por la que $1debe incluirse entre comillas es que si no existe $1, el shell intentará ejecutarse if [ == "-h" ]y fallará porque ==solo se le ha dado un solo argumento cuando esperaba dos:

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

Para cualquier uso más complejo getoptogetopts

Según lo sugerido por otros , si tiene más de una opción simple, o necesita su opción para aceptar un argumento, entonces definitivamente debe optar por la complejidad adicional del uso getopts.

Como referencia rápida, me gusta el tutorial de 60 segundos de getopts .

También puede considerar el getoptprograma en lugar del shell integrado getopts. Permite el uso de opciones largas y opciones después de argumentos sin opciones (por ejemplo, en foo a b c --verboselugar de solo foo -v a b c). Esta respuesta de Stackoverflow explica cómo usar GNU getopt.

jeffbyrnes mencionó que el enlace original murió pero afortunadamente la forma en que la máquina lo había archivado.

Mark Booth
fuente
¡Gracias! He estado felizmente usando getopts durante un año, pero también echaré un vistazo a getopt.
tttppp
1
Lamentablemente, el enlace al Tutorial de los 60 segundos getopts está muerto; parece que bashcurescancer.com ya no existe. Aquí hay un enlace a la versión de la máquina Wayback .
jeffbyrnes
-1

Creo que puedes usar el caso para esto ...

case $1 in 
 -h) echo $usage ;; 
  h) echo $usage ;;
help) echo $usage ;;
esac
Nuevo Usuario
fuente