Ich schrieb ein Skript in Bash dient als Vorlage für mehrere Monitore. Ich wähle getopt, um lange Optionen auf CLI verwenden zu können. Ich habe jedoch einige Probleme, es richtig zu implementieren.Getopt nicht gut analysieren Bash
Das gesamte Skript ist viel länger, aber das ist der relevante Teil:
#!/bin/bash
#
# FUNCTION
# main
# DESCRIPTION
# Main function. Everything will be called from here
# ARGS
# Nothing
#
# RETURN CODE
# Nothing
#
# Main function. Everything will be called from here
main() {
# Parse the options and arguments
parse_options "${@}"
# Check if the interval is set to a valid number
check_interval
}
#
# FUNCTION
# check_interval
# DESCRIPTION
# Checks if a number is valid
# ARGS
# 1: number to be checked
# RETURN CODE
# 0: valid
# 1: invalid
#
check_interval() {
# We don't have to worry if interval is set at all, because getopt is already doing this
if (! check_number_pos ${arginterval}); then
echo "Error: invalid interval: ${arginterval}"
show_usage
exit 2
fi
}
#
# FUNCTION
# show_usage
# DESCRIPTION
# This is the Usage section. We a showing the Usage according to docopt standards
# ARGS
# Nothing
# RETURN CODE
# Nothing
#
show_usage() {
echo "Usage:" >&2
echo " ${THIS_SCRIPT_NAME} -i|--interval=<interval in s> [-r | --random [--randomwait=<wait in s>]] [-v|--verbose] [-d|--debug] [--colors]" >&2
echo " ${THIS_SCRIPT_NAME} [-h|--help]" >&2
}
#
# FUNCTION
# check_number_pos
# DESCRIPTION
# Checks if a number is valid and positive
# ARGS
# 1: number to be checked
#
# RETURN CODE
# 0: valid and positive
# 1: invalid or negative
#
check_number_pos() {
local returnval
if [[ "${1}" =~ ^[0-9]+$ ]]; then
returnval=0
else
returnval=1
fi
return ${returnval}
}
#
# FUNCTION
# parse_options
# DESCRIPTION
# Parse options from command line
# ARGS
# @: Arguments and options as given at CLI
# RETURN CODE
# Nothing
#
parse_options() {
# Use getopt(1) to parse options according to POSIX. If it fails, an error is shown, and we're showing the Usage and exit
# Add new options here and also in the case-statement below.
# Short options must be added in the 'options'-section
# Long options must be added in the 'longoptions'-section
# All short options must have a long equivalent
# The --name is set so the error-output will not show 'getopt'-errors but neat <application name>-errors
# Options and longoptions have the following format:
# <letter> Option without argument
# <letter>: Option with mandarory argument
# <letter>:: Option with optional argument <- this is broken for short options and long options without '='. Don't use it!
local -r GETOPT=$(getopt --name ${0} --options hrvdi: --longoptions help,random,verbose,debug,colors,randomwait:,interval: -- "${@}")
if [ ${?} != 0 ]; then
echo "Error: Error while getting arguments"
show_usage
exit 127;
fi
# No options or arguments given. Show Usage.
if [[ "${GETOPT}" == " --" ]]; then
show_usage
exit 127;
fi
# Evaluate GETOPT. We need this to have the quotes in the output of getopt(1) interpreted.
eval set -- "${GETOPT}"
# Walk through all the options. Don't put too much code in here, just point to a function or set a variable.
# Please note, all new options need to be added here but also in the GETOPT line above.
# Note: shift removes the first value from the string, so the option itself will be removed from the GETOPT-string, and the argument is available in $1
# After using an argument, please shift again, so the next option will be the first value in GETOPT
while true;
do
case "${1}" in
-i|--interval)
shift
arginterval=${1}
shift
;;
-r|--random)
shift
flagrandom=1
;;
--randomwait)
shift
flagrandom=1
argrandom=${1}
shift
;;
-v|-d|--verbose|--debug)
flagdebug=1
shift
;;
--colors)
flagcolors=1
shift
;;
-h|--help)
#show_help
exit 0
;;
--)
shift
break
;;
-*)
echo "Error: unrecognized option ${1}"
show_usage
exit 127
;;
*)
show_usage
exit 127
;;
esac
done
}
#Call main function after all
main "${@}"
Nun, wenn ich das Skript den richtigen Weg nennen, geht alles glatt:
$ ./test1.sh -i 10
Als ich vergiss das argument, es macht auch was ich will:
$ ./test1.sh -i
./test1.sh: option requires an argument -- 'i'
Usage:
-i|--interval=<interval in s> [-r | --random [--randomwait=<wait in s>]] [-v|--verbose] [-d|--debug] [--colors]
[-h|--help]
Aber wenn ich einfach das Argument vergesse und ein addiere andere, es scheitert:
$ ./test1.sh -i --colors
Error: invalid interval: --colors
Usage:
-i|--interval=<interval in s> [-r | --random [--randomwait=<wait in s>]] [-v|--verbose] [-d|--debug] [--colors]
[-h|--help]
Dies geschieht, weil ich, wenn das Intervall prüfe eine ganze Zahl ist, sondern auch für andere Zwecke, dies ist eine gefährliche Sache Wie kann ich den Fall zu ändern, so dass es die Optionen als Argumente nicht gelesen werden ? Bisher dient mir getopt nicht sehr gut, da ich auch auf einen anderen Bug/Feature stieß: das 'optionale Argument' (: :) funktioniert nicht so, wie ich es erwartet habe, da es nur mit einem '=' zwischen Option funktioniert und Argument.
Versionen:
$ getopt -V
getopt (enhanced) 1.1.4
$ bash --version
GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)
In der Tat; in ähnlicher Weise erfordert 'getup', dass ein _optional_ option-Argument direkt an die Option _ angehängt wird - ohne Trennzeichen in der Kurzform (zB' -i10'), mit '=' als Trennzeichen in der langen Form (zB '- -interval = 10') - weil das der einzige unmissverständliche Weg ist, es zu tun. – mklement0
Ok, vielleicht versuche ich, die Gedanken zu narrensicher zu machen, aber ich habe erwartet, dass getopt versteht, dass - Farben eine "Option" sind, weil ich es ihm gesagt habe. Ich werde dem Skript weitere Überprüfungen hinzufügen, um fehlerhafte Eingaben zu vermeiden. –
'--colors' können eine Option sein, aber es gibt keine Möglichkeit, 'getopt' zu sagen, dass es nicht auch ein gültiges Argument für' -i' sein kann. – chepner