Passando argumentos nomeados como array no shell script

4

Eu tenho este código em uma ferramenta que estou construindo atualmente:

while [ $# -gt 0 ]; do
  case "$1" in
    --var1=*)
      var1="${1#*=}"
      ;;
    --var2=*)
      var1="${1#*=}"
      ;;
    --var3=*)
      var1="${1#*=}"
      ;;
    *)
      printf "***************************\n
              * Error: Invalid argument.*\n
              ***************************\n"
  esac
  shift
done

Eu tenho muitas opções para adicionar, mas cinco das minhas opções devem ser salvas como matrizes. Então, se eu chamar a ferramenta, vamos dizer a partir do shell usando algo como isto: ./tool --var1="2" --var1="3" --var1="4" --var1="5" --var2="6" --var3="7"

Como posso salvar o valor de var1 como uma matriz? Isso é possível? E, em caso afirmativo, qual é a melhor maneira de lidar com esses arrays em termos de eficiência se eu tiver muitos deles?

    
por Ahmad alkaid 22.10.2015 / 12:46

2 respostas

6

Se no Linux (com os utilitários util-linux incluindo getopt instalado ou o de busybox ), você pode fazer:

declare -A opt_spec
var1=() var2=() var4=false
unset var3
opt_spec=(
  [opt1:]='var1()' # opt with argument, array variable
  [opt2:]='var2()' # ditto
  [opt3:]='var3'   # opt with argument, scalar variable
  [opt4]='var4'    # boolean opt without argument
)
parsed_opts=$(
  IFS=,
  getopt -o + -l "${!opt_spec[*]}" -- "$@"
) || exit
eval "set -- $parsed_opts"
while [ "$#" -gt 0 ]; do
  o=$1; shift
  case $o in
    (--) break;;
    (--*)
      o=${o#--}
      if ((${opt_spec[$o]+1})); then # opt without argument
        eval "${opt_spec[$o]}=true"
      else
        o=$o:
        case "${opt_spec[$o]}" in
          (*'()') eval "${opt_spec[$o]%??}+=(\"\\")";;
          (*) eval "${opt_spec[$o]}=\"
        esac
        shift
      fi
  esac
done
echo "var1: ${var1[@]}"

Dessa forma, você pode chamar seu script como:

my-script --opt1=foo --opt2 bar --opt4 -- whatever

E o getopt fará o trabalho duro de analisá-lo, manipulando -- e abreviações para você.

Como alternativa, você pode confiar no tipo da variável, em vez de especificá-la na sua definição de matriz associativa $opt_spec :

declare -A opt_spec
var1=() var2=() var4=false
unset var3
opt_spec=(
  [opt1:]=var1   # opt with argument
  [opt2:]=var2   # ditto
  [opt3:]=var3   # ditto
  [opt4]=var4    # boolean opt without argument
)
parsed_opts=$(
  IFS=,
  getopt -o + -l "${!opt_spec[*]}" -- "$@"
) || exit
eval "set -- $parsed_opts"
while [ "$#" -gt 0 ]; do
  o=$1; shift
  case $o in
    (--) break;;
    (--*)
      o=${o#--}
      if ((${opt_spec[$o]+1})); then # opt without argument
        eval "${opt_spec[$o]}=true"
      else
        o=$o:
        case $(declare -p "${opt_spec[$o]}" 2> /dev/null) in
          ("declare -a"*) eval "${opt_spec[$o]}+=(\"\\")";;
          (*) eval "${opt_spec[$o]}=\"
        esac
        shift
      fi
  esac
done
echo "var1: ${var1[@]}"

Você pode adicionar opções curtas como:

declare -A long_opt_spec short_opt_spec
var1=() var2=() var4=false
unset var3
long_opt_spec=(
  [opt1:]=var1   # opt with argument
  [opt2:]=var2   # ditto
  [opt3:]=var3   # ditto
  [opt4]=var4    # boolean opt without argument
)
short_opt_spec=(
  [a:]=var1
  [b:]=var2
  [c]=var3
  [d]=var4
)
parsed_opts=$(
  IFS=; short_opts="${!short_opt_spec[*]}"
  IFS=,
  getopt -o "+$short_opts" -l "${!long_opt_spec[*]}" -- "$@"
) || exit
eval "set -- $parsed_opts"
while [ "$#" -gt 0 ]; do
  o=$1; shift
  case $o in
    (--) break;;
    (--*)
      o=${o#--}
      if ((${long_opt_spec[$o]+1})); then # opt without argument
        eval "${long_opt_spec[$o]}=true"
      else
        o=$o:
        case $(declare -p "${long_opt_spec[$o]}" 2> /dev/null) in
          ("declare -a"*) eval "${long_opt_spec[$o]}+=(\"\\")";;
          (*) eval "${long_opt_spec[$o]}=\"
        esac
        shift
      fi;;
    (-*)
      o=${o#-}
      if ((${short_opt_spec[$o]+1})); then # opt without argument
        eval "${short_opt_spec[$o]}=true"
      else
        o=$o:
        case $(declare -p "${short_opt_spec[$o]}" 2> /dev/null) in
          ("declare -a"*) eval "${short_opt_spec[$o]}+=(\"\\")";;
          (*) eval "${short_opt_spec[$o]}=\"
        esac
        shift
      fi
  esac
done
echo "var1: ${var1[@]}"
    
por 22.10.2015 / 13:20
-1

Sugiro que você dê uma olhada no meu Script geral do Shell GitHub: utility_functions.sh . Lá você verá uma função chamada getArgs . Ele é projetado para associar opções e valores.

Estou colando aqui apenas a função, mas isso depende de mais algumas funções dentro desse script

##########################
#
#   Function name: getArgs
#
#   Description:
#       This function provides the getopts functionality
#   while allowing the use of long operations and list of parameters.
#   in the case of a list of arguments for only one option, this list
#   will be returned as a single-space-separated list in one single string.
#
#   Pre-reqs:
#       None
#
#   Output:
#       GA_OPTION variable will hold the current option
#       GA_VALUE variable will hold the value (or list of values) associated
#           with the current option
#   
#   Usage:
#       You have to source the function in order to be able to access the GA_OPTIONS
#   and GA_VALUES variables
#       . getArgs $*
#
####################
function getArgs {

    # Variables to return the values out of the function
    typeset -a GA_OPTIONS
    typeset -a GA_VALUES

    # Checking for number of arguments
    if [[ -z $1 ]]
    then
        msgPrint -warning "No arguments found"
        msgPrint -info "Please call this function as follows: . getArgs \$*"
        exit 0
    fi

    # Grab the dash
    dash=$(echo $1 | grep "-")
    # Looking for short (-) or long (--) options
    isOption=$(expr index "$dash" "-")
    # Initialize the counter
    counter=0
    # Loop while there are arguments left
    while [[ $# -gt 0 ]]
    do
        if [[ $dash && $isOption -eq 1 ]]
        then
            (( counter+=1 ))
            GA_OPTIONS[$counter]=$1
            shift
        else
            if [[ -z ${GA_VALUES[$counter]} ]]
            then
                GA_VALUES[$counter]=$1
            else
                GA_VALUES[$counter]="${GA_VALUES[$counter]} $1"
            fi
            shift
        fi
        dash=$(echo $1 | grep "-")
        isOption=$(expr index "$dash" "-")
    done
    # Make the variables available to the main algorithm
    export GA_OPTIONS
    export GA_VALUES

    msgPrint -debug "Please check the GA_OPTIONS and GA_VALUES arrays for options and arguments"
    # Exit with success
    return 0
}

Como você pode ver, essa função específica exportará GA_OPTIONS e GA_VALUES. A única condição para isso é que os valores devem ser uma lista separada por espaço após a opção.

Você chamaria o script como ./tool --var1 2 3 4 5 --var2="6" --var3="7"

Ou use uma lógica semelhante para acomodar suas preferências.

Espero que isso ajude.

    
por 23.10.2015 / 07:58