theme - Como obter argumentos com sinalizadores no Bash




install bash it ubuntu (5)

Este é o idioma que eu geralmente uso:

while test $# -gt 0; do
        case "$1" in
                -h|--help)
                        echo "$package - attempt to capture frames"
                        echo " "
                        echo "$package [options] application [arguments]"
                        echo " "
                        echo "options:"
                        echo "-h, --help                show brief help"
                        echo "-a, --action=ACTION       specify an action to use"
                        echo "-o, --output-dir=DIR      specify a directory to store output in"
                        exit 0
                        ;;
                -a)
                        shift
                        if test $# -gt 0; then
                                export PROCESS=$1
                        else
                                echo "no process specified"
                                exit 1
                        fi
                        shift
                        ;;
                --action*)
                        export PROCESS=`echo $1 | sed -e 's/^[^=]*=//g'`
                        shift
                        ;;
                -o)
                        shift
                        if test $# -gt 0; then
                                export OUTPUT=$1
                        else
                                echo "no output dir specified"
                                exit 1
                        fi
                        shift
                        ;;
                --output-dir*)
                        export OUTPUT=`echo $1 | sed -e 's/^[^=]*=//g'`
                        shift
                        ;;
                *)
                        break
                        ;;
        esac
done

Pontos-chave são:

  • $# é o número de argumentos
  • while loop analisa todos os argumentos fornecidos, correspondendo em seus valores dentro de uma instrução case
  • turno leva o primeiro embora. Você pode mudar várias vezes dentro de uma instrução case para obter vários valores.

https://code.i-harness.com

Eu sei que posso facilmente obter parâmetros posicionados como este no bash:

$0 ou $1

Eu quero ser capaz de usar opções de sinalizador como este para especificar o que cada parâmetro é usado:

mysql -u user -h host

Qual é a melhor maneira de obter o valor do -u param e o valor do -h param por flag em vez de por posição?


Este exemplo usa o comando getopts do Bash e é do Guia de estilo do Google Shell :

a_flag=''
b_flag=''
files=''
verbose='false'

print_usage() {
  printf "Usage: ..."
}

while getopts 'abf:v' flag; do
  case "${flag}" in
    a) a_flag='true' ;;
    b) b_flag='true' ;;
    f) files="${OPTARG}" ;;
    v) verbose='true' ;;
    *) print_usage
       exit 1 ;;
  esac
done

Nota: Se um caractere for seguido por dois-pontos (por exemplo, f: , espera-se que essa opção tenha um argumento.

Exemplo de uso: ./script -v -a -b -f filename

Usando getopts tem várias vantagens sobre a resposta aceita:

  • a condição while é muito mais legível e mostra quais são as opções aceitas
  • código mais limpo; sem contar o número de parâmetros e deslocamento
  • você pode unir opções (por exemplo -a -b -c-abc )

No entanto, uma grande desvantagem é que ele não suporta opções longas, apenas opções de um único caractere.


Eu gosto da resposta de Robert McMahan o melhor aqui, pois parece o mais fácil de fazer em incluir arquivos compartilháveis ​​para qualquer um dos seus scripts para usar. Mas parece ter uma falha com a linha if [[ -n ${variables[$argument_label]} ]] jogando a mensagem, "variáveis: subscrito de matriz ruim". Eu não tenho o representante para comentar, e duvido que esta seja a 'correção' apropriada, mas envolvendo isso if if [[ -n $argument_label ]] ; then if [[ -n $argument_label ]] ; then limpa.

Aqui está o código que acabei, se você conhece uma maneira melhor, por favor, adicione um comentário à resposta de Robert.

Incluir arquivo "flags-declares.sh"

# declaring a couple of associative arrays
declare -A arguments=();
declare -A variables=();

# declaring an index integer
declare -i index=1;

Incluir Arquivo "flags-arguments.sh"

# [email protected] here represents all arguments passed in
for i in "[email protected]"
do
  arguments[$index]=$i;
  prev_index="$(expr $index - 1)";

  # this if block does something akin to "where $i contains ="
  # "%=*" here strips out everything from the = to the end of the argument leaving only the label
  if [[ $i == *"="* ]]
    then argument_label=${i%=*}
    else argument_label=${arguments[$prev_index]}
  fi

  if [[ -n $argument_label ]] ; then
    # this if block only evaluates to true if the argument label exists in the variables array
    if [[ -n ${variables[$argument_label]} ]] ; then
      # dynamically creating variables names using declare
      # "#$argument_label=" here strips out the label leaving only the value
      if [[ $i == *"="* ]]
        then declare ${variables[$argument_label]}=${i#$argument_label=} 
        else declare ${variables[$argument_label]}=${arguments[$index]}
      fi
    fi
  fi

  index=index+1;
done;

Seu "script.sh"

. bin/includes/flags-declares.sh

# any variables you want to use here
# on the left left side is argument label or key (entered at the command line along with it's value) 
# on the right side is the variable name the value of these arguments should be mapped to.
# (the examples above show how these are being passed into this script)
variables["-gu"]="git_user";
variables["--git-user"]="git_user";
variables["-gb"]="git_branch";
variables["--git-branch"]="git_branch";
variables["-dbr"]="db_fqdn";
variables["--db-redirect"]="db_fqdn";
variables["-e"]="environment";
variables["--environment"]="environment";

. bin/includes/flags-arguments.sh

# then you could simply use the variables like so:
echo "$git_user";
echo "$git_branch";
echo "$db_fqdn";
echo "$environment";

Outra alternativa seria usar algo como o exemplo abaixo, que permitiria que você usasse as tags long -image ou short -i e também permitisse métodos compilados -i = "example.jpg" ou separado -i example.jpg de passar argumentos .

# declaring a couple of associative arrays
declare -A arguments=();  
declare -A variables=();

# declaring an index integer
declare -i index=1;

# any variables you want to use here
# on the left left side is argument label or key (entered at the command line along with it's value) 
# on the right side is the variable name the value of these arguments should be mapped to.
# (the examples above show how these are being passed into this script)
variables["-gu"]="git_user";  
variables["--git-user"]="git_user";  
variables["-gb"]="git_branch";  
variables["--git-branch"]="git_branch";  
variables["-dbr"]="db_fqdn";  
variables["--db-redirect"]="db_fqdn";  
variables["-e"]="environment";  
variables["--environment"]="environment";

# [email protected] here represents all arguments passed in
for i in "[email protected]"  
do  
  arguments[$index]=$i;
  prev_index="$(expr $index - 1)";

  # this if block does something akin to "where $i contains ="
  # "%=*" here strips out everything from the = to the end of the argument leaving only the label
  if [[ $i == *"="* ]]
    then argument_label=${i%=*} 
    else argument_label=${arguments[$prev_index]}
  fi

  # this if block only evaluates to true if the argument label exists in the variables array
  if [[ -n ${variables[$argument_label]} ]]
    then
        # dynamically creating variables names using declare
        # "#$argument_label=" here strips out the label leaving only the value
        if [[ $i == *"="* ]]
            then declare ${variables[$argument_label]}=${i#$argument_label=} 
            else declare ${variables[$argument_label]}=${arguments[$index]}
        fi
  fi

  index=index+1;
done;

# then you could simply use the variables like so:
echo "$git_user";

getopt é seu amigo .. um exemplo simples:

function f () {
TEMP=`getopt --long -o "u:h:" "[email protected]"`
eval set -- "$TEMP"
while true ; do
    case "$1" in
        -u )
            user=$2
            shift 2
        ;;
        -h )
            host=$2
            shift 2
        ;;
        *)
            break
        ;;
    esac 
done;

echo "user = $user, host = $host"
}

f -u myself -h some_host

Deve haver vários exemplos em seu diretório / usr / bin.







shell