Donner à un script bash l'option d'accepter des indicateurs, comme une commande [duplicate]
J'écris des scripts bash simples et j'aimerais qu'il accepte les paramètres de la ligne de commande dans n'importe quel ordre.
J'ai parcouru le Web et écrit une fonction simple avec une déclaration de cas dans une boucle while. À l'heure actuelle, la partie «toute commande» fonctionne - mais elle ne prend que le premier paramètre que j'ai défini. Je fais certainement quelque chose de mal, mais le script est assez nouveau pour moi et je n'avais pas été en mesure de le comprendre - votre aide serait grandement appréciée. La partie flags du script est la suivante:
#Parameters - source,destination,credentials,bandwidth,timeout,port,help
flags () {
while test $# -gt 0; do case "$1" in
-s|--source)
shift
if test $# -gt 0; then export SOURCE=$1
else
echo "No source directory specified!"
exit 1
fi
;;
-d|--destination)
shift
if test $# -gt 0; then export DESTINATION=$1
fi
;;
-c|--credentials)
shift
if test $# -gt 0; then export CREDENTIALS=$1
fi
;;
-b|--bandwidth)
shift
if test $# -gt 0; then export BANDWIDTH=$1
fi
;;
-t|--timeout)
shift
if test $# -gt 0; then export TIMEOUT=$1
fi
;;
-p|--port)
shift
if test $# -gt 0; then export PORT=$1
fi
;;
-h|--help)
shift
if test $# -gt 0; then echo "Help goes here" fi ;; -l|--compression-level) shift if test $# -gt 0; then
export COMPRESS_LEVEL=$1 fi ;; *) break ;; esac done } flags "$@"
echo "source is $SOURCE, destination is $DESTINATION, credentials are $CREDENTIALS, bandwidth is $BANDWIDTH, timeout is $TIMEOUT, port is $PORT"
Idéalement, certains de ces paramètres seraient obligatoires et d'autres facultatifs, mais ce n'est pas obligatoire.
Comment puis-je corriger ce script pour accepter l'un de ces paramètres (à la fois les formes longues et courtes, idéalement) dans n'importe quel ordre?
Merci!
Réponses
Comme indiqué dans les commentaires, après avoir utilisé l'argument (par exemple pour les informations d'identification), vous avez besoin d'un autre quart de travail. Vous devez être cohérent dans votre rapport d'erreurs pour les arguments inexistants. Si vous obtenez -hou --help, vous devez simplement imprimer l'aide et quitter; vous ne devriez pas tester plus d'arguments. Si de l'aide est demandée, vous la donnez et ne faites rien d'autre. Vous devez également l' écho des erreurs d'erreur standard: echo "message" >&2. Vos messages doivent être précédés du nom du script / programme: et etc.arg0=$(basename "$0" .sh)echo "$arg0: message" >&2
En rassemblant les modifications, vous pourriez créer un script comme celui-ci:
#!/bin/sh
arg0=$(basename "$0" .sh) blnk=$(echo "$arg0" | sed 's/./ /g') usage_info() { echo "Usage: $arg0 [{-s|--source} source] [{-d|--destination} destination] \\"
echo " $blnk [{-c|--credentials} credentials] [{-b|--bandwidth} bandwidth] \\" echo " $blnk [{-t|--timeout} timeout] [{-p|--port} port] \\"
echo " $blnk [-h|--help] [{-l|--compression-level} level]" } usage() { exec 1>2 # Send standard output to standard error usage_info exit 1 } error() { echo "$arg0: $*" >&2 exit 1 } help() { usage_info echo echo " {-s|--source} source -- Set source directory (default: .)" echo " {-d|--destination} destination -- Set destination" echo " {-c|--credentials} credentials -- Set credentials" echo " {-b|--bandwidth} bandwidth -- Set maximum bandwidth" echo " {-t|--timeout} timeout -- Set timeout (default: 60s)" echo " {-p|--port} port -- Set port number (default: 1234)" echo " {-l|--compression-level} level -- Set compression level (default: 1)" echo " {-h|--help} -- Print this help message and exit" # echo " {-V|--version} -- Print version information and exit" exit 0 } flags() { while test $# -gt 0
do
case "$1" in (-s|--source) shift [ $# = 0 ] && error "No source directory specified"
export SOURCE="$1" shift;; (-d|--destination) shift [ $# = 0 ] && error "No destination specified"
export DESTINATION="$1" shift;; (-c|--credentials) shift [ $# = 0 ] && error "No credentials specified"
export CREDENTIALS="$1" shift;; (-b|--bandwidth) shift [ $# = 0 ] && error "No bandwidth specified"
export BANDWIDTH="$1" shift;; (-t|--timeout) shift [ $# = 0 ] && error "No timeout specified"
export TIMEOUT="$1" shift;; (-p|--port) shift [ $# = 0 ] && error "No port specified"
export PORT="$1" shift;; (-l|--compression-level) shift [ $# = 0 ] && error "No compression level specified"
export COMPRESS_LEVEL="$1" shift;; (-h|--help) help;; # (-V|--version) # version_info;; (*) usage;; esac done } flags "$@"
echo "source is $SOURCE" echo "destination is $DESTINATION"
echo "credentials are $CREDENTIALS" echo "bandwidth is $BANDWIDTH"
echo "timeout is $TIMEOUT" echo "port is $PORT"
Exemple d'exécution (nom du script:) flags53.sh:
$ sh flags53.sh -c XYZ -d PQR -s 123 -l 4 -t 99 -b 12 -p 56789 source is 123 destination is PQR credentials are XYZ bandwidth is 12 timeout is 99 port is 56789 $ sh flags53.sh -c XYZ --destination PQR -s 123 -l 4 --timeout 99 -b 12 --port 56789
source is 123
destination is PQR
credentials are XYZ
bandwidth is 12
timeout is 99
port is 56789
$ sh flags53.sh -c XYZ -h Usage: flags53 [{-s|--source} source] [{-d|--destination} destination] \ [{-c|--credentials} credentials] [{-b|--bandwidth} bandwidth] \ [{-t|--timeout} timeout] [{-p|--port} port] \ [-h|--help] [{-l|--compression-level} level] {-s|--source} source -- Set source directory (default: .) {-d|--destination} destination -- Set destination {-c|--credentials} credentials -- Set credentials {-b|--bandwidth} bandwidth -- Set maximum bandwidth {-t|--timeout} timeout -- Set timeout (default: 60s) {-p|--port} port -- Set port number (default: 1234) {-l|--compression-level} level -- Set compression level (default: 1) {-h|--help} -- Print this help message and exit $
Notez que l'aide demandée peut aller à la sortie standard au lieu de l'erreur standard, bien que l'envoi de l'aide à l'erreur standard ne soit pas un crime flagrant. L'aide obtient le message d'utilisation et des informations supplémentaires sur la signification de chaque option. Noter les valeurs par défaut (et les définir) est également une bonne idée. Il peut ne pas être nécessaire d'exporter les paramètres - vous pouvez simplement définir les variables sans explicite export. Vous devez vraiment définir les variables à leurs valeurs par défaut avant d'appeler la flagsfonction, ou au début de la flagsfonction. Cela évite d'hériter accidentellement des valeurs exportées (variables d'environnement). À moins, bien sûr, que vous ne vouliez accepter des variables d'environnement, mais alors vos noms devraient probablement recevoir un préfixe systématique approprié pour le nom du script. La plupart des programmes devraient avoir une option --versionou -V(à utiliser -vpour «verbose», pas pour la version). Si la commande n'accepte aucun argument sans option (nom de fichier), ajoutez une coche après la boucle d'analyse et vous plaignez des arguments indésirables. Si la commande doit avoir au moins un argument sans option, vérifiez-le à la place. Ne signalez pas une erreur lors de la réception --en argument; terminer la boucle de vérification et traiter tous les arguments restants comme des arguments sans option.
Un problème résiduel - les changements dans la fonction affectent la liste d'arguments de la fonction, pas le global "$@". You'd have to work out how to deal with that from this skeleton. I think I'd probably create an analogue to $OPTIND qui indique le nombre d'arguments à déplacer pour accéder aux arguments sans option. Le code de la fonction flags doit garder une trace du nombre d'arguments qu'il change.
Cela conduit au code révisé:
#!/bin/sh
arg0=$(basename "$0" .sh)
blnk=$(echo "$arg0" | sed 's/./ /g')
usage_info()
{
echo "Usage: $arg0 [{-s|--source} source] [{-d|--destination} destination] \\" echo " $blnk [{-c|--credentials} credentials] [{-b|--bandwidth} bandwidth] \\"
echo " $blnk [{-t|--timeout} timeout] [{-p|--port} port] \\" echo " $blnk [-h|--help] [{-l|--compression-level} level]"
}
usage()
{
exec 1>2 # Send standard output to standard error
usage_info
exit 1
}
error()
{
echo "$arg0: $*" >&2
exit 1
}
help()
{
usage_info
echo
echo " {-s|--source} source -- Set source directory (default: .)"
echo " {-d|--destination} destination -- Set destination"
echo " {-c|--credentials} credentials -- Set credentials"
echo " {-b|--bandwidth} bandwidth -- Set maximum bandwidth"
echo " {-t|--timeout} timeout -- Set timeout (default: 60s)"
echo " {-p|--port} port -- Set port number (default: 1234)"
echo " {-l|--compression-level} level -- Set compression level (default: 1)"
echo " {-h|--help} -- Print this help message and exit"
# echo " {-V|--version} -- Print version information and exit"
exit 0
}
flags()
{
OPTCOUNT=0
while test $# -gt 0 do case "$1" in
(-s|--source)
shift
[ $# = 0 ] && error "No source directory specified" export SOURCE="$1"
shift
OPTCOUNT=$(($OPTCOUNT + 2));;
(-d|--destination)
shift
[ $# = 0 ] && error "No destination specified" export DESTINATION=$1
shift
OPTCOUNT=$(($OPTCOUNT + 2));;
(-c|--credentials)
shift
[ $# = 0 ] && error "No credentials specified" export CREDENTIALS=$1
shift
OPTCOUNT=$(($OPTCOUNT + 2));;
(-b|--bandwidth)
shift
[ $# = 0 ] && error "No bandwidth specified" export BANDWIDTH=$1
shift
OPTCOUNT=$(($OPTCOUNT + 2));;
(-t|--timeout)
shift
[ $# = 0 ] && error "No timeout specified" export TIMEOUT="$1"
shift
OPTCOUNT=$(($OPTCOUNT + 2));;
(-p|--port)
shift
[ $# = 0 ] && error "No port specified" export PORT=$1
shift
OPTCOUNT=$(($OPTCOUNT + 2));;
(-l|--compression-level)
shift
[ $# = 0 ] && error "No compression level specified" export COMPRESS_LEVEL="$1"
shift
OPTCOUNT=$(($OPTCOUNT + 2));;
(-h|--help)
help;;
# (-V|--version)
# version_info;;
(--)
shift
OPTCOUNT=$(($OPTCOUNT + 1))
break;;
(*) usage;;
esac
done
echo "DEBUG-1: [$*]" >&2 echo "OPTCOUNT=$OPTCOUNT" >&2
}
flags "$@" echo "DEBUG-2: [$*]" >&2
echo "OPTCOUNT=$OPTCOUNT" >&2 shift $OPTCOUNT
echo "DEBUG-3: [$*]" >&2 echo "source is $SOURCE"
echo "destination is $DESTINATION" echo "credentials are $CREDENTIALS"
echo "bandwidth is $BANDWIDTH" echo "timeout is $TIMEOUT"
echo "port is $PORT"
Il existe d'autres manières d'écrire l'arithmétique si vous souhaitez expérimenter. N'utilisez pas exprcependant.