Как получить аргументы с флагами в Bash - PullRequest
240 голосов
/ 15 августа 2011

Я знаю, что могу легко получить позиционированные параметры, подобные этим, в bash:

$0 или $1

Я хочу иметь возможность использовать такие параметры флага, чтобы указать длякакой каждый параметр используется:

mysql -u user -h host

Каков наилучший способ получить значение -u param и значение -h param по флагу, а не по позиции?

Ответы [ 7 ]

345 голосов
/ 15 января 2014

В этом примере используется встроенная в Bash команда getopts, созданная в Руководстве по стилю 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

Примечание. Еслиза символом следует двоеточие (например, f:), этот параметр должен иметь аргумент.

Пример использования: ./script -v -a -b -f filename

Использование getopts имеет несколько преимуществ по сравнению спринятый ответ:

  • условие while гораздо более читабельно и показывает, какие допустимые варианты имеют
  • код уборщика;не считая количества параметров и сдвигов
  • вы можете объединить опции (например, -a -b -c-abc)

Однако, большим недостатком является то, что он не поддерживает долгоопции, только односимвольные опции.

246 голосов
/ 15 августа 2011

Я обычно использую эту идиому:

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

Ключевые моменты:

  • $# - количество аргументов
  • цикл while просматривает все предоставленные аргументы, совпадая по их значениям внутри оператора case
  • Сдвиг убирает первого. Вы можете смещаться несколько раз внутри оператора case, чтобы принимать несколько значений.
42 голосов
/ 16 августа 2011

getopt ваш друг .. простой пример:

function f () {
TEMP=`getopt --long -o "u:h:" "$@"`
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

В каталоге / usr / bin должны быть различные примеры.

7 голосов
/ 09 августа 2017

Я думаю, это послужило бы более простым примером того, чего вы хотите достичь.Нет необходимости использовать внешние инструменты.Встроенные инструменты Bash могут выполнить эту работу за вас.

function DOSOMETHING {

   while test $# -gt 0; do
           case "$1" in
                -first)
                    shift
                    first_argument=$1
                    shift
                    ;;
                -last)
                    shift
                    last_argument=$1
                    shift
                    ;;
                *)
                   echo "$1 is not a recognized flag!"
                   return 1;
                   ;;
          esac
  done  

  echo "First argument : $first_argument";
  echo "Last argument : $last_argument";
 }

Это позволит вам использовать флаги, поэтому независимо от того, в каком порядке вы передаете параметры, вы получите правильное поведение.

Пример:

 DOSOMETHING -last "Adios" -first "Hola"

Вывод:

 First argument : Hola
 Last argument : Adios

Вы можете добавить эту функцию в свой профиль или поместить в сценарий.

Спасибо!

Редактировать: сохранить это как файл и затем выполнить как yourfile.sh -last "Adios" -first "Hola"

#!/bin/bash
while test $# -gt 0; do
           case "$1" in
                -first)
                    shift
                    first_argument=$1
                    shift
                    ;;
                -last)
                    shift
                    last_argument=$1
                    shift
                    ;;
                *)
                   echo "$1 is not a recognized flag!"
                   return 1;
                   ;;
          esac
  done  

  echo "First argument : $first_argument";
  echo "Last argument : $last_argument";
5 голосов
/ 28 мая 2016

Другой альтернативой может быть использование чего-то вроде приведенного ниже примера, который позволит вам использовать длинные - image или короткие -i теги, а также разрешить скомпилированные -i = "example.jpg" или отдельный -i example.jpg методы передачи аргументов.

# 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";

# $@ here represents all arguments passed in
for i in "$@"  
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";
3 голосов
/ 02 марта 2018

Мне больше всего нравится ответ Роберта МакМахана, поскольку его проще всего превратить в файлы общего доступа для любых ваших сценариев.Но, похоже, есть недостаток в том, что строка if [[ -n ${variables[$argument_label]} ]] выдает сообщение «переменные: неверный индекс массива».У меня нет представителя, чтобы комментировать, и я сомневаюсь, что это правильное «исправление», но завершение, которое if в if [[ -n $argument_label ]] ; then очищает его.

Вот код, с которым я закончил, еслиВы знаете лучший способ, пожалуйста, добавьте комментарий к ответу Роберта.

Включить файл "flags-declares.sh"

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

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

Включить файл "flags-arguments.sh"

# $@ here represents all arguments passed in
for i in "$@"
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;

Ваш "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";
1 голос
/ 04 мая 2018

Если вы знакомы с Python argparse и не против вызвать python для разбора аргументов bash, есть фрагмент кода, который я считаю действительно полезным и очень простым в использовании, называемый argparse-bash https://github.com/nhoffman/argparse-bash

Пример берут из их скрипта example.sh:

#!/bin/bash

source $(dirname $0)/argparse.bash || exit 1
argparse "$@" <<EOF || exit 1
parser.add_argument('infile')
parser.add_argument('outfile')
parser.add_argument('-a', '--the-answer', default=42, type=int,
                    help='Pick a number [default %(default)s]')
parser.add_argument('-d', '--do-the-thing', action='store_true',
                    default=False, help='store a boolean [default %(default)s]')
parser.add_argument('-m', '--multiple', nargs='+',
                    help='multiple values allowed')
EOF

echo required infile: "$INFILE"
echo required outfile: "$OUTFILE"
echo the answer: "$THE_ANSWER"
echo -n do the thing?
if [[ $DO_THE_THING ]]; then
    echo " yes, do it"
else
    echo " no, do not do it"
fi
echo -n "arg with multiple values: "
for a in "${MULTIPLE[@]}"; do
    echo -n "[$a] "
done
echo
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...