Pregunta Shell Script: ¿es posible mezclar getopts con parámetros posicionales?


Quiero diseñar un script de shell como un contenedor para un par de scripts. Me gustaría especificar los parámetros para myshell.sh utilizando getopts y pase los parámetros restantes en el mismo orden al script especificado.

Si myshell.sh se ejecuta como:

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

Todo lo anterior debe poder llamar como

test.sh param1 param2 param3

¿Es posible utilizar los parámetros de opciones en  myshell.sh  y publica los parámetros restantes en el script subyacente?


40
2017-07-31 14:58


origen


Respuestas:


Perdón por comentar sobre un hilo viejo, pero pensé en postear para aquellos, como yo, que estábamos buscando cómo hacer esto ...

Quería hacer algo similar al OP, y encontré la información relevante que requería aquí y aquí 

Básicamente, si quieres hacer algo como:

script.sh [options] ARG1 ARG2

A continuación, obtenga sus opciones de esta manera:

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

Y luego puedes obtener tus argumentos posicionales como este:

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

Más información y detalles están disponibles a través del enlace de arriba.

¡¡Espero que ayude!!


74
2017-11-15 15:04



Mix opts y 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

Resultado:

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

4
2017-10-22 10:20



getopts no analizará la mezcla de param1 y -n opciones.

Es mucho mejor poner param1-3 en opciones como otras.

Además, puede usar bibliotecas ya existentes como Shflags. Es bastante inteligente y fácil de usar.

Y la última forma es escribir su propia función para analizar params sin getopts, simplemente iterando todos los parámetros a través de case construcción. Es la forma más difícil, pero es la única manera de hacer coincidir exactamente sus expectativas.


3
2017-07-31 15:21



Pensé de una manera que getopts se puede extender para mezclar realmente opciones y parámetros posicionales. La idea es alternar entre llamadas getopts y asignando cualquier parámetro posicional encontrado para 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
}

Luego, en el caso del OP, podrías usarlo así:

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 "$@"

Al ejecutarlo, se muestra el resultado:

$ 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

Solo una prueba de concepto, pero tal vez sea útil para alguien.

Nota: hay una gotcha si una función que utiliza parse_args llama a otra función que utiliza parse_args  y la función externa declara, p. local n4='', pero el interno no y 4 o más parámetros de posición se pasan a la función interna


1
2017-08-31 20:48



Solo prepare un quickie, que maneja fácilmente una mezcla de opciones y parámetros posicionales (dejando solo parametros posicionales en $ @):

#!/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

Muestra de salida:

[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
2017-08-03 02:41



Existen algunos estándares para el procesamiento de opciones de Unix, y en la programación de shell, getopts es la mejor manera de hacerlos valer. Casi cualquier lenguaje moderno (perl, python) tiene una variante en getopts.

Este es solo un ejemplo rápido:

command [ options ] [--] [ words ]
  1. Cada opción debe comenzar con un guion, -, y debe consistir en un solo personaje.

  2. El proyecto GNU introdujo Long Options, comenzando con dos guiones --, seguido de una palabra completa, --long_option. El proyecto AST KSH tiene un getopts que también admite opciones largas, y opciones largas comenzando con un solo guion, -, como en find(1) .

  3. Las opciones pueden o no esperar argumentos.

  4. Cualquier palabra que no comience con un guion, -, finalizará el procesamiento de opciones.

  5. La cuerda -- debe omitirse y finalizará el procesamiento de la opción.

  6. Los argumentos restantes se dejan como parámetros posicionales.

The Open Group tiene una sección de Utility Argument Syntax

The Art of Unix Programming de Eric Raymond tiene un capitulo en opciones de Unix tradicionales para letras de opción y su significado.


0
2018-02-14 10:38



Puedes probar este truco: después de while loop con optargs, solo usa este fragmento

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

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

POSITIONAL="$@"

Sin embargo, este enfoque tiene un error:

    todas las opciones después del primer argumento posicional son asumidas por getopts y son consideradas como argumentos posicionales - event aquellas que son correctas (ver resultado de la muestra: -m y -c están entre los argumentos posicionales)

Tal vez tiene incluso más errores ...

Mira el ejemplo completo:

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"

Salida:

[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



Preguntas populares