with script receive manage bash shell parameters

bash - receive - shell script arguments



Shell Script: ¿es posible mezclar getopts con parámetros posicionales? (7)

Existen algunos estándares para el procesamiento de opciones de Unix, y en la programación de shell, getopts es la mejor manera de aplicarlos. 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 guión, - , y debe consistir en un solo carácter.

  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 que comienzan con un solo guión, - , como en find(1) .

  3. Las opciones pueden o no esperar argumentos.

  4. Cualquier palabra que no comience con un guión, - , terminará el procesamiento de la opción.

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

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

Open Group tiene una sección sobre la Utility Argument Syntax

The Art of Unix Programming, de Eric Raymond, tiene un capítulo sobre las opciones tradicionales de Unix para las letras de opción y su significado.

Quiero diseñar un script de shell como un contenedor para un par de scripts. Me gustaría especificar parámetros para myshell.sh utilizando getopts y pasar 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 publicar los parámetros restantes en el script subyacente?


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


Pensé en una forma en que getopts se puede extender para realmente mezclar opciones y parámetros posicionales. La idea es alternar entre getopts llamada y asignar cualquier parámetro posicional encontrado a 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 un error si una función que usa parse_args llama a otra función que usa parse_args y la función externa declara, por ejemplo, local n4='''' , pero la interna no y 4 o más parámetros posicionales pasan a la función interna


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 here y here

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


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


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:~]$


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

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 la construcción del case . Es la forma más difícil, pero es la única manera de hacer coincidir exactamente sus expectativas.