J'essaie de construire un script shell qui accepte diverses options et getopts
semble être une bonne solution car il peut gérer l'ordre des variables des options et des arguments (je pense!).
Je n'utiliserai que des options courtes et chaque option courte nécessitera une valeur correspondante, par exemple: ./command.sh -a arga -g argg -b argb
mais je voudrais autoriser la saisie des options dans un ordre non spécifique, comme c'est la façon dont la plupart des gens sont habitués à travailler avec des commandes shell .
L'autre point est que je voudrais faire ma propre vérification des valeurs des arguments d'option, idéalement dans les case
instructions. La raison en est que mes tests :)
dans ma case
déclaration ont donné des résultats incohérents (probablement par manque de compréhension de ma part).
Par exemple:
#!/bin/bash
OPTIND=1 # Reset if getopts used previously
if (($# == 0)); then
echo "Usage"
exit 2
fi
while getopts ":h:u:p:d:" opt; do
case "$opt" in
h)
MYSQL_HOST=$OPTARG
;;
u)
MYSQL_USER=$OPTARG
;;
p)
MYSQL_PASS=$OPTARG
;;
d)
BACKUP_DIR=$OPTARG
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 2;;
:)
echo "Option -$OPTARG requires an argument" >&2
exit 2;;
esac
done
shift $((OPTIND-1))
echo "MYSQL_HOST='$MYSQL_HOST' MYSQL_USER='$MYSQL_USER' MYSQL_PASS='$MYSQL_PASS' BACKUP_DIR='$BACKUP_DIR' Additionals: $@"
Échouait pour des événements comme celui-ci ... ./command.sh -d -h
Quand je veux qu'il marque -d comme nécessitant un argument, mais j'obtiens la valeur -d=-h
dont je n'ai pas besoin.
J'ai donc pensé qu'il serait plus facile d'exécuter ma propre validation dans les déclarations de cas pour garantir que chaque option n'est définie et définie qu'une seule fois.
J'essaie de faire ce qui suit mais mes if [ ! "$MYSQL_HOST" ]; then
blocs ne sont pas déclenchés.
OPTIND=1 # Reset if getopts used previously
if (($# == 0)); then
echo "Usage"
exit 2
fi
while getopts ":h:u:p:d:" opt; do
case "$opt" in
h)
MYSQL_HOST=$OPTARG
if [ ! "$MYSQL_HOST" ]; then
echo "host not set"
exit 2
fi
;;
u)
MYSQL_USER=$OPTARG
if [ ! "$MYSQL_USER" ]; then
echo "username not set"
exit 2
fi
;;
p)
MYSQL_PASS=$OPTARG
if [ ! "$MYSQL_PASS" ]; then
echo "password not set"
exit 2
fi
;;
d)
BACKUP_DIR=$OPTARG
if [ ! "$BACKUP_DIR" ]; then
echo "backup dir not set"
exit 2
fi
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 2;;
#:)
# echo "Option -$opt requires an argument" >&2
# exit 2;;
esac
done
shift $((OPTIND-1))
echo "MYSQL_HOST='$MYSQL_HOST' MYSQL_USER='$MYSQL_USER' MYSQL_PASS='$MYSQL_PASS' BACKUP_DIR='$BACKUP_DIR' Additionals: $@"
Y a-t-il une raison pour laquelle je ne peux pas vérifier si un OPTARG
a une longueur nulle de l'intérieur getopts ... while ... case
?
Quelle est la meilleure façon d'exécuter ma propre validation d'argument getopts
dans un cas où je ne veux pas compter sur le :)
. Effectuer ma validation d'argument en dehors du while ... case ... esac
?
Ensuite, je pourrais me retrouver avec des valeurs d'argument de -d
etc et ne pas attraper une option manquante.
while
boucle qui parcourait les options. C'est la méthode que j'ai choisie car bien qu'elle soit verbeuse par rapport aux autres, c'est très clair ce qui se passe dans le script. Et la maintenabilité par d'autres est importante dans ce cas.Étant donné que les autres réponses ne répondent pas vraiment à votre question: il s'agit d'un comportement attendu et basé sur la façon dont POSIX est interprété:
C'est tout ce qui est énoncé, et pour faire court (pas si long):
getopts
ne sait pas comme par magie que l'argument parfaitement valide qu'il voit pour l'option dont vous avez besoin pour avoir un argument n'est en fait pas un argument d'option mais une autre option . Rien ne vous empêche d'avoir-h
comme argument valide l'-d
option, ce qui signifie en pratique quegetopts
ne lancera une erreur que si votre option qui nécessite un argument vient en dernier sur la ligne de commande, par exemple:test.sh
:Exemple:
La raison pour laquelle votre deuxième approche ne fonctionne pas est que l'analyse syntaxique
getopts
est toujours la même, donc une fois que vous êtes dans la boucle, le "dommage" est déjà fait.Si vous voulez absolument interdire cela, alors, comme l'a souligné Benubird, vous devrez vérifier vous-même vos arguments d'option et lancer une erreur s'ils équivalent à une option valide.
la source
Je continuerais à utiliser,
getopts
mais ferais quelques vérifications supplémentaires après: (non testé)nécessite bash version 4
la source
Le getopt intégré non shell de Gnu fait moins de travail pour vous mais permet une plus grande flexibilité à mesure que vous déterminez ce qui se passe après qu'un indicateur est trouvé, y compris en désactivant vous-même les arguments.
J'ai trouvé un article comparant getopts et getopt qui sera probablement utile car le manuel est un peu difficile à comprendre (au moins avant le café).
la source
Tout d'abord, vous devez utiliser
if [ -z "$MYSQL_USER" ]
.Deuxièmement, la tâche n'est pas nécessaire -
if [ -z "$OPTARG" ]
fonctionnera bien.Troisièmement, je soupçonne que vous voulez vraiment
if [ ${#OPTARG} = 0 ]
. $ {# x} est une chose bash qui retourne la longueur de la chaîne $ x (voir plus ici ).Quatrièmement, si vous effectuez votre propre validation, je recommanderais getopt au lieu de getopts, car cela offre beaucoup plus de flexibilité.
Enfin, pour répondre à votre première question, vous pouvez détecter quand un indicateur est passé en argument en plaçant une liste d'indicateurs en haut, comme ceci:
Et puis avoir une vérification dans l'option où vous voulez vérifier si l'argument fourni est une option, quelque chose comme ceci:
Pas une réponse parfaite, mais ça marche! Votre problème est que "-h" est un argument parfaitement valide, et vous ne donnez aucun moyen au shell de savoir qu'il ne recherche que des paramètres qui ne sont pas également des indicateurs valides.
la source