84 votes

Est-il possible de mélanger des getopts avec des paramètres de position ?

Je veux concevoir un script shell comme une enveloppe pour un couple de script. J'aimerais spécifier des paramètres pour myshell.sh en utilisant getopts et transmet les autres paramètres dans le même ordre au script spécifié.

Si myshell.sh est exécuté comme suit :

myshell.sh -h hostname -s test.sh -d waittime param1 param2 param3

myshell.sh param1 param2 -h hostname param3 -d waittime -s test.sh

myshell.sh param1 -h hostname -d waittime -s test.sh param2 param3

Tous les éléments ci-dessus devraient pouvoir être appelés en tant que

test.sh param1 param2 param3

Est-il possible d'utiliser les paramètres d'options dans le fichier myshell.sh et envoyer les paramètres restants au script sous-jacent ?

113voto

DRendar Points 331

Je voulais faire quelque chose de similaire à l'OP, et j'ai trouvé les informations pertinentes dont j'avais besoin aquí y aquí

Essentiellement, si vous voulez faire quelque chose comme.. :

script.sh [options] ARG1 ARG2

Ensuite, vous obtenez vos options de la manière suivante :

while getopts "h:u:p:d:" flag; do
case "$flag" in
    h) HOSTNAME=$OPTARG;;
    u) USERNAME=$OPTARG;;
    p) PASSWORD=$OPTARG;;
    d) DATABASE=$OPTARG;;
esac
done

Ensuite, vous pouvez obtenir vos arguments de position comme suit :

ARG1=${@:$OPTIND:1}
ARG2=${@:$OPTIND+1:1}

Plus d'informations et de détails sont disponibles sur le lien ci-dessus.

J'espère que cela vous aidera !

27voto

chichi Points 271

Mélanger les options et les arguments :

ARGS=""
echo "options :"
while [ $# -gt 0 ]
do
    unset OPTIND
    unset OPTARG
    while getopts as:c:  options
    do
    case $options in
            a)  echo "option a  no optarg"
                    ;;
            s)  serveur="$OPTARG"
                    echo "option s = $serveur"
                    ;;
            c)  cible="$OPTARG"
                    echo "option c = $cible"
                    ;;
        esac
   done
   shift $((OPTIND-1))
   ARGS="${ARGS} $1 "
   shift
done

echo "ARGS : $ARGS"
exit 1

Résultat :

bash test.sh  -a  arg1 arg2 -s serveur -c cible  arg3
options :
option a  no optarg
option s = serveur
option c = cible
ARGS :  arg1  arg2  arg3

24voto

Chris Pearson Points 341

Myshell.sh :

#!/bin/bash

script_args=()
while [ $OPTIND -le "$#" ]
do
    if getopts h:d:s: option
    then
        case $option
        in
            h) host_name="$OPTARG";;
            d) wait_time="$OPTARG";;
            s) script="$OPTARG";;
        esac
    else
        script_args+=("${!OPTIND}")
        ((OPTIND++))
    fi
done

"$script" "${script_args[@]}"

test.sh :

#!/bin/bash
echo "$0 $@"

Test des cas de l'OP :

$ PATH+=:.  # Use the cases as written without prepending ./ to the scripts
$ myshell.sh -h hostname -s test.sh -d waittime param1 param2 param3
./test.sh param1 param2 param3
$ myshell.sh param1 param2 -h hostname param3 -d waittime -s test.sh
./test.sh param1 param2 param3
$ myshell.sh param1 -h hostname -d waittime -s test.sh param2 param3
./test.sh param1 param2 param3

Ce qui se passe :

getopts échouera s'il rencontre un paramètre positionnel. S'il est utilisé comme condition de boucle, la boucle s'interrompt prématurément lorsque des paramètres de position apparaissent avant les options, comme c'est le cas dans deux des cas de test.

Ainsi, cette boucle ne s'interrompt que lorsque tous les paramètres ont été traités. Si getopts ne reconnaît pas quelque chose, nous supposons simplement qu'il s'agit d'un paramètre positionnel, et nous le plaçons dans un tableau tout en incrémentant manuellement getopts Le comptoir de l'entreprise.

Améliorations possibles :

Tel qu'il est écrit, le script enfant ne peut pas accepter d'options (uniquement des paramètres de position), puisque getopts dans le wrapper script les mangera et affichera un message d'erreur, tout en traitant tout argument comme un paramètre positionnel :

$ myshell.sh param1 param2 -h hostname -d waittime -s test.sh -a opt1 param3
./myshell.sh: illegal option -- a
./test.sh param1 param2 opt1 param3

Si nous savons que l'enfant script ne peut accepter que des paramètres positionnels, alors myshell.sh devrait probablement s'arrêter sur une option non reconnue. Cela pourrait être aussi simple que d'ajouter un dernier cas par défaut à la fin de la chaîne de caractères case bloc :

            \?) exit 1;;

$ myshell.sh param1 param2 -h hostname -d waittime -s test.sh -a opt1 param3
./myshell.sh: illegal option -- a

Si le script enfant doit accepter des options (tant qu'elles n'entrent pas en conflit avec les options de la rubrique myshell.sh ), nous pourrions changer getopts pour rendre silencieux le rapport d'erreur en faisant précéder la chaîne d'options de deux points :

    if getopts :h:d:s: option

Ensuite, nous utiliserons le dernier cas par défaut pour placer toute option non reconnue dans script_args :

            \?) script_args+=("-$OPTARG");;

$ myshell.sh param1 param2 -h hostname -d waittime -s test.sh -a opt1 param3
./test.sh param1 param2 -a opt1 param3

3voto

rush Points 2031

getopts n'analysera pas le mélange de param1 y -n options.

Il est préférable de placer les paramètres 1 à 3 dans des options comme les autres.

En outre, vous pouvez utiliser des bibliothèques existantes telles que drapeaux . Il est assez intelligent et facile à utiliser.

Et la dernière façon est d'écrire votre propre fonction pour analyser les paramètres sans getopts, en itérant tous les paramètres à travers case construction. C'est le moyen le plus difficile, mais c'est le seul moyen de répondre exactement à vos attentes.

2voto

Michael Kropat Points 3993

J'ai imaginé un moyen pour que getopts peut être étendu à de véritables options de mélange et à des paramètres de position. L'idée est d'alterner entre l'appel à getopts et en attribuant tous les paramètres de position trouvés à n1 , n2 , n3 etc :

parse_args() {
    _parse_args 1 "$@"
}

_parse_args() {
    local n="$1"
    shift

    local options_func="$1"
    shift

    local OPTIND
    "$options_func" "$@"
    shift $(( OPTIND - 1 ))

    if [ $# -gt 0 ]; then
        eval test -n \${n$n+x}
        if [ $? -eq 0 ]; then
            eval n$n="\$1"
        fi

        shift
        _parse_args $(( n + 1 )) "$options_func" "$@"
    fi
}

Dans le cas de l'OP, vous pourriez l'utiliser comme suit :

main() {
    local n1='' n2='' n3=''
    local duration hostname script

    parse_args parse_main_options "$@"

    echo "n1 = $n1"
    echo "n2 = $n2"
    echo "n3 = $n3"
    echo "duration = $duration"
    echo "hostname = $hostname"
    echo "script   = $script"
}

parse_main_options() {
    while getopts d:h:s: opt; do
        case "$opt" in
            d) duration="$OPTARG" ;;
            h) hostname="$OPTARG" ;;
            s) script="$OPTARG"   ;;
        esac
    done
}

main "$@"

En l'exécutant, on obtient le résultat suivant :

$ myshell.sh param1 param2 -h hostname param3 -d waittime -s test.sh
n1 = param1
n2 = param2
n3 = param3
duration = waittime
hostname = hostname
script   = test.sh

Il s'agit juste d'une preuve de concept, mais peut-être que cela peut être utile à quelqu'un.

Remarque : il y a un problème si une fonction qui utilise parse_args appelle une autre fonction qui utilise parse_args y la fonction extérieure déclare par exemple local n4='' mais pas l'intérieur y 4 paramètres positionnels ou plus sont transmis à la fonction interne

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X