Como posso adicionar um método de ajuda a um script de shell?

128

Como verifico se um -hatributo foi passado para um script de shell? Gostaria de exibir uma mensagem de ajuda quando um usuário ligar myscript.sh -h.

tttppp
fonte
Exemplos relevantes: stackoverflow.com/questions/14008125/…
Anton Tarasenko 22/10

Respostas:

173

Aqui está um exemplo para o 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 isso dentro de uma função:

  • use em "$FUNCNAME"vez de$(basename "$0")
  • adicione local OPTIND OPTARGantes de ligargetopts
Glenn Jackman
fonte
1
Estou tentando isso dentro de uma função, mas quando tento executar a função, recebo este erro "basename: opção inválida - 'b'". Parece que está tentando passar o "-bash" para basenameo traço principal.
Morgan Estes
5
imside uma função "$FUNCNAME"não use "$0". Além disso, addlocal OPTIND OPTARG
glenn jackman
Obrigado. FUNCNAMEtrabalho. Eu tenho todas as minhas funções em um único arquivo, então isso é perfeito para estendê-las para algo útil para outras pessoas.
Morgan Estes
5
@sigur, certifique-se de citar "$usage" todos os lugares em que você o usa.
Glenn Jackman
1
Para que serve shift $((OPTIND - 1))?
hpaknia
45

O primeiro argumento para um script de shell está disponível como variável $1, portanto, a implementação mais simples seria

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

Mas o que anubhava disse.

seb
fonte
Graças @MarkBooth, typo corrigido (mais melhoria de envolvimento entre aspas)
seb
Você deve criar o hábito de [...] if em condicionais para evitar uma análise ruim de uma variável, fonte: github.com/bahamas10/bash-style-guide#bashisms
JREAM
2
Sim, embora o OP não tenha especificado bash e [seja a versão compatível com POSIX.
seb
Nota - Para uso interno function: você deve substituir exit 0por returnse não quiser finalizar seu shell após executar sua função. (Eu já fiz isso antes 😂) #
Illuminator
29

aqui está uma parte que eu uso para iniciar um 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

É um pouco estranho que eu coloquei o start stop restart em um caso separado, mas deve funcionar

Vincent Stans
fonte
se você der uma opção vazia para -d; não será loop infinito?
Zerobane
Por que você sai 1 na função auxiliar?
jeantimex
17

Para uma solução rápida de opção única, use if

Se você tiver apenas uma única opção para verificar e sempre será a primeira opção ( $1), a solução mais simples será a ifcom um teste ( [). Por exemplo:

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

Observe que a compatibilidade com o posix =também funcionará ==.

Por que citar $1?

A razão pela qual as $1aspas precisam ser colocadas entre aspas é que, se não houver $1, o shell tentará executar if [ == "-h" ]e falhar, porque ==recebeu apenas um argumento quando esperava dois:

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

Para algo mais complexo, use getoptougetopts

Conforme sugerido por outros , se você tiver mais do que uma única opção simples ou precisar da sua opção para aceitar um argumento, você definitivamente deve procurar a complexidade extra do uso getopts.

Como referência rápida, eu gosto do tutorial Getopts de 60 segundos .

Você também pode considerar o getoptprograma em vez do shell interno getopts. Permite o uso de opções longas e opções após argumentos não opcionais (por exemplo, foo a b c --verboseao invés de apenas foo -v a b c). Esta resposta do Stackoverflow explica como usar o GNU getopt.

jeffbyrnes mencionou que o link original morreu, mas felizmente o caminho de volta foi arquivado pela máquina .

Mark Booth
fonte
Obrigado! Estou usando felizmente getopts há um ano, mas também darei uma olhada no getopt.
Ttpppp 01/03
1
Infelizmente, o link para o Tutorial de 60 segundos getopts está morto; parece que bashcurescancer.com não existe mais. Aqui está um link para a versão do Wayback Machine .
jeffbyrnes
-1

Eu acho que você pode usar caso para isso ...

case $1 in 
 -h) echo $usage ;; 
  h) echo $usage ;;
help) echo $usage ;;
esac
novo usuário
fonte