Jak mogę dodać metodę pomocy do skryptu powłoki?

131

Jak sprawdzić, czy -hatrybut został przekazany do skryptu powłoki? Chciałbym wyświetlić komunikat pomocy, gdy dzwoni użytkownik myscript.sh -h.

tttppp
źródło
Odpowiednie przykłady: stackoverflow.com/questions/14008125/…
Anton Tarasenko

Odpowiedzi:

177

oto przykład dla basha:

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))

Aby użyć tego wewnątrz funkcji:

  • użyj "$FUNCNAME"zamiast$(basename "$0")
  • dodaj local OPTIND OPTARGprzed zadzwonieniemgetopts
glenn jackman
źródło
1
Próbuję tego wewnątrz funkcji, ale kiedy próbuję uruchomić funkcję, pojawia się komunikat o błędzie: „basename: invalid option - 'b'”. Wygląda na to, że próbuje przekazać „-bash” do basenamez początkowym myślnikiem.
Morgan Estes
5
imside funkcja "$FUNCNAME"nie używa "$0". Dodaj teżlocal OPTIND OPTARG
glenn jackman,
Dzięki. FUNCNAMEPracuje. Mam wszystkie moje funkcje w jednym pliku, więc jest to idealne rozwiązanie do rozszerzenia ich na coś przydatnego dla innych.
Morgan Estes
5
@sigur, zacytuj "$usage" każde miejsce, w którym go używasz.
glenn jackman
1
Do czego służy shift $((OPTIND - 1))?
hpaknia
46

Pierwszy argument skryptu powłoki jest dostępny jako zmienna $1, więc najprostsza byłaby implementacja

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

Ale co powiedział Anubhava.

seb
źródło
Powinieneś mieć zwyczaj zawijania if w [...]] dla warunków warunkowych, aby uniknąć złego parsowania zmiennej, źródło: github.com/bahamas10/bash-style-guide#bashisms
JREAM
2
Tak, chociaż OP nie określa bash i [jest wersją zgodną z POSIX.
seb
Uwaga - W przypadku korzystania wewnątrz function: Należy wymienić exit 0ze returnjeśli nie chcesz, aby zakończyć swoją skorupę po uruchomić funkcję. (Robiłem to wcześniej 😂)
Illuminator,
29

tutaj jest część, której używam do uruchomienia serwera 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

To trochę dziwne, że umieściłem start stop restart w osobnym przypadku, ale powinno działać

Vincent Stans
źródło
jeśli dasz pustą opcję -d; czy nie będzie to nieskończona pętla?
zerobane
Dlaczego wychodzisz z 1 w funkcji pomocniczej?
jeantimex
17

Aby uzyskać szybkie rozwiązanie z jedną opcją, użyj if

Jeśli masz tylko jedną opcję do sprawdzenia i zawsze będzie to pierwsza opcja ( $1), najprostszym rozwiązaniem jest metoda ifz testem ( [). Na przykład:

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

Zauważ, że dla kompatybilności z posix =zadziała również ==.

Dlaczego cytować $1?

Powodem, $1dla którego należy ująć w cudzysłów, jest to, że jeśli nie ma, $1to powłoka spróbuje uruchomić się if [ == "-h" ]i zawiedzie, ponieważ ==otrzymała tylko jeden argument, gdy oczekiwała dwóch:

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

Do bardziej złożonych zastosowań getoptlubgetopts

Jak sugerowano przez innych , jeśli masz więcej niż jedną opcję prostego lub potrzebujesz opcję przyjąć argumentu, to powinno się iść do dodatkowej złożoności użyciu getopts.

Krótko mówiąc, podoba mi się samouczek dotyczący 60 sekund getopts .

Możesz także rozważyć getoptprogram zamiast wbudowanej powłoki getopts. Pozwala na użycie długich opcji i opcji po argumentach innych niż opcje (np. foo a b c --verboseRaczej niż tylko foo -v a b c). Ta odpowiedź Stackoverflow wyjaśnia, jak używać GNU getopt.

jeffbyrnes wspomniał, że oryginalny link zgasł, ale na szczęście maszyna zarchiwizowała go.

Mark Booth
źródło
Dzięki! Od roku szczęśliwie używam getopts, ale przyjrzę się też getopt.
tttppp
1
Niestety, link do samouczka The 60 Second getopts jest martwy; wygląda na to, że bashcurescancer.com już nie istnieje. Oto link do wersji Wayback Machine .
jeffbyrnes
-1

myślę, że możesz użyć tego przypadku ...

case $1 in 
 -h) echo $usage ;; 
  h) echo $usage ;;
help) echo $usage ;;
esac
nowy użytkownik
źródło