web-dev-qa-db-fra.com

Shell Script: est-il possible de mélanger getopts avec des paramètres de position?

Je veux concevoir un script Shell comme wrapper pour quelques scripts. Je voudrais spécifier les paramètres pour myshell.sh en utilisant getopts et transmettre les paramètres restants dans le même ordre au script spécifié.

Si myshell.sh est exécuté comme:

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

Tout ce qui précède devrait pouvoir appeler en tant que

test.sh param1 param2 param3

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

44
SiB

Désolé de commenter un ancien fil de discussion, mais je pensais poster pour ceux qui, comme moi, cherchaient comment faire cela ...

Je voulais faire quelque chose de similaire au PO et j'ai trouvé les informations pertinentes dont j'avais besoin ici et ici

Essentiellement, si vous voulez faire quelque chose comme:

script.sh [options] ARG1 ARG2

Ensuite, obtenez vos options comme ceci:

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

Et alors vous pouvez obtenir vos arguments de position comme ceci:

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

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

J'espère que cela pourra aider!!

77
DRendar

Mix opts et args: 

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
6
chichi

getopts n'analysera pas le mélange des options param1 et -n.

Il est bien mieux de mettre param1-3 dans des options comme les autres.

De plus, vous pouvez utiliser des bibliothèques déjà existantes telles que shflags . C'est très intelligent et facile à utiliser.

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

3
rush

J'ai imaginé l'une des façons dont getopts peut être étendu pour vraiment mélanger les options et les paramètres de position. L'idée est d'alterner entre l'appel getopts et l'attribution des 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
}

Ensuite, dans le cas du PO, vous pourriez l'utiliser comme:

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 cours d'exécution, il affiche la sortie:

$ 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

Juste une preuve de concept, mais peut-être utile pour quelqu'un.

Remarque: il y a un casse-tête si une fonction qui utilise parse_args appelle une autre fonction qui utilise parse_args et la fonction externe déclare par exemple. local n4='', mais le paramètre interne pas et 4 ou plusieurs paramètres de position sont transmis à la fonction interne

1
Michael Kropat

Il suffit de réduire un quickie, qui gère facilement un mélange d'options et de paramètres de position (ne laissant que les paramètres de position dans $ @):

#!/bin/bash
while [ ${#} -gt 0 ];do OPTERR=0;OPTIND=1;getopts "p:o:hvu" arg;case "$arg" in
        p) echo "Path:   [$OPTARG]" ;;
        o) echo "Output: [$OPTARG]" ;;
        h) echo "Help"              ;;
        v) echo "Version"           ;;
    \?) SET+=("$1")                                           ;;
    *) echo "Coding error: '-$arg' is not handled by case">&2 ;;
esac;shift;[ "" != "$OPTARG" ] && shift;done
[ ${#SET[@]} -gt 0 ] && set "" "${SET[@]}" && shift

echo -e "=========\nLeftover (positional) parameters (count=$#) are:"
for i in `seq $#`;do echo -e "\t$i> [${!i}]";done

Exemple de sortie:

[root@hots:~]$ ./test.sh 'aa bb' -h -v -u -q 'cc dd' -p 'ee ff' 'gg hh' -o ooo
Help
Version
Coding error: '-u' is not handled by case
Path:   [ee ff]
Output: [ooo]
=========
Leftover (positional) parameters (count=4) are:
        1> [aa bb]
        2> [-q]
        3> [cc dd]
        4> [gg hh]
[root@hots:~]$
1
Vlad

Vous pouvez essayer cette astuce: après la boucle while avec optargs, utilisez simplement cet extrait.

#shift away all the options so that only positional agruments
#remain in $@

for (( i=0; i<OPTIND-1; i++)); do
    shift
done

POSITIONAL="$@"

Cependant, cette approche a un bug:

    toutes les options situées après le premier argument de position sont incorporées par getopts et sont considérées comme des arguments de position. Évidemment, elles sont correctes (voir l'exemple de résultat: -m et -c figurent parmi les arguments de position)

Peut-être qu'il a encore plus de bugs ...

Regardez tout l'exemple:

while getopts :abc opt; do
    case $opt in
        a)
        echo found: -a
        ;;
        b)
        echo found: -b
        ;;
        c)
        echo found: -c
        ;;
        \?) echo found bad option: -$OPTARG
        ;;
    esac
done

#OPTIND-1 now points to the first arguments not beginning with -

#shift away all the options so that only positional agruments
#remain in $@

for (( i=0; i<OPTIND-1; i++)); do
    shift
done

POSITIONAL="$@"

echo "positional: $POSITIONAL"

Sortie:

[root@Host ~]# ./abc.sh -abc -de -fgh -bca haha blabla -m -c
found: -a
found: -b
found: -c
found bad option: -d
found bad option: -e
found bad option: -f
found bad option: -g
found bad option: -h
found: -b
found: -c
found: -a
positional: haha blabla -m -c
0
Yuriy Pozniak

Il existe certaines normes pour le traitement des options Unix, et dans la programmation Shell, getopts est le meilleur moyen de les appliquer. Presque tous les langages modernes (Perl, python) ont une variante sur getopts

Ceci est juste un exemple rapide:

command [ options ] [--] [ words ]
  1. Chaque option doit commencer par un tiret, -, et doit consister en un seul caractère.

  2. Le projet GNU a introduit Long Options, en commençant par deux tirets --, Suivi d'un mot entier, --long_option. Le projet AST KSH comporte un getopts qui prend également en charge les options longues, et les options longues, commençant par un tiret unique, -, comme dans find(1).

  3. Les options peuvent ou ne peuvent pas attendre des arguments.

  4. Tout mot ne commençant pas par un tiret, -, mettra fin au traitement des options.

  5. La chaîne -- doit être ignorée et mettra fin au traitement des options.

  6. Tous les arguments restants sont laissés en tant que paramètres de position.

Le groupe ouvert a une section sur Utility Argument Syntax

L'art de la programmation Unix d'Eric Raymond a un chapitre sur les choix Unix traditionnels pour les lettres d'option et leur signification.

0
Henk Langeveld