case, аналогичные, но разные варианты назначения переменной [duplicate]

Наряду с попыткой всего, что предлагает Teo Chuen Wei Bryan, убедитесь, что вы также ссылаетесь на правильное имя Server / Instance в строке подключения.

Если вы используете краткую форму имени хоста / экземпляра на сервере базы данных или в файле web.config, убедитесь, что вы используете полное доменное имя (FQDN) / экземпляр

Кроме того, чтобы проверить подключение с сервера, на котором нет клиента SQL-сервера,

-> создать текстовый файл и изменить его расширение файла на .udl

-> Щелкните правой кнопкой мыши файл, и вы увидите вкладку подключения.

-> Введите имя сервера и зарегистрируйте информацию для проверки соединения с сервером базы данных.

Надеюсь, это поможет.

1421
задан codeforester 25 January 2017 в 06:38
поделиться

29 ответов

Метод # 1: Использование bash без getopt [s]

Два общих способа передачи аргументов пары ключ-значение:

Bash Space-Separated (например, --option argument) (без getopt [s])

Использование ./myscript.sh -e conf -s /etc -l /usr/lib /etc/hosts

#!/bin/bash

POSITIONAL=()
while [[ $# -gt 0 ]]
do
key="$1"

case $key in
    -e|--extension)
    EXTENSION="$2"
    shift # past argument
    shift # past value
    ;;
    -s|--searchpath)
    SEARCHPATH="$2"
    shift # past argument
    shift # past value
    ;;
    -l|--lib)
    LIBPATH="$2"
    shift # past argument
    shift # past value
    ;;
    --default)
    DEFAULT=YES
    shift # past argument
    ;;
    *)    # unknown option
    POSITIONAL+=("$1") # save it in an array for later
    shift # past argument
    ;;
esac
done
set -- "${POSITIONAL[@]}" # restore positional parameters

echo FILE EXTENSION  = "${EXTENSION}"
echo SEARCH PATH     = "${SEARCHPATH}"
echo LIBRARY PATH    = "${LIBPATH}"
echo DEFAULT         = "${DEFAULT}"
echo "Number files in SEARCH PATH with EXTENSION:" $(ls -1 "${SEARCHPATH}"/*."${EXTENSION}" | wc -l)
if [[ -n $1 ]]; then
    echo "Last line of file specified as non-opt/last argument:"
    tail -1 "$1"
fi

Bash Equals-Separated (например, --option=argument) (без getopt [s])

Использование ./myscript.sh -e=conf -s=/etc -l=/usr/lib /etc/hosts

#!/bin/bash

for i in "$@"
do
case $i in
    -e=*|--extension=*)
    EXTENSION="${i#*=}"
    shift # past argument=value
    ;;
    -s=*|--searchpath=*)
    SEARCHPATH="${i#*=}"
    shift # past argument=value
    ;;
    -l=*|--lib=*)
    LIBPATH="${i#*=}"
    shift # past argument=value
    ;;
    --default)
    DEFAULT=YES
    shift # past argument with no value
    ;;
    *)
          # unknown option
    ;;
esac
done
echo "FILE EXTENSION  = ${EXTENSION}"
echo "SEARCH PATH     = ${SEARCHPATH}"
echo "LIBRARY PATH    = ${LIBPATH}"
echo "Number files in SEARCH PATH with EXTENSION:" $(ls -1 "${SEARCHPATH}"/*."${EXTENSION}" | wc -l)
if [[ -n $1 ]]; then
    echo "Last line of file specified as non-opt/last argument:"
    tail -1 $1
fi

Чтобы лучше понять ${i#*=}, найдите «Удаление подстроки» в этом руководстве . Он функционально эквивалентен `sed 's/[^=]*=//' <<< "$i"`, который вызывает ненужный подпроцесс или `echo "$i" | sed 's/[^=]*=//'`, который вызывает два ненужных подпроцесса.

Использование getopt [s]

из: http://mywiki.wooledge.org/BashFAQ/035#getopts

Ограничения getopt (1) (более старые, относительно недавние версии getopt):

  • не может обрабатывать аргументы, которые являются пустыми строками
  • не может обрабатывать аргументы со встроенными whitespace

Более новые версии getopt не имеют этих ограничений.

Кроме того, оболочка POSIX (и другие) предлагает getopts, которая не имеет эти ограничения. Ниже приведен упрощенный пример getopts:

#!/bin/sh

# A POSIX variable
OPTIND=1         # Reset in case getopts has been used previously in the shell.

# Initialize our own variables:
output_file=""
verbose=0

while getopts "h?vf:" opt; do
    case "$opt" in
    h|\?)
        show_help
        exit 0
        ;;
    v)  verbose=1
        ;;
    f)  output_file=$OPTARG
        ;;
    esac
done

shift $((OPTIND-1))

[ "${1:-}" = "--" ] && shift

echo "verbose=$verbose, output_file='$output_file', Leftovers: $@"

# End of file

Преимущества getopts:

  1. Это более портативный и будет работать в других оболочках, таких как dash.
  2. Он может обрабатывать несколько отдельных параметров, таких как -vf filename в обычном способе Unix, автоматически.

Недостатком getopts является то, что он может обрабатывать только короткие варианты (-h, а не --help) без дополнительного кода.

Существует учебник getopts , который объясняет, что означает весь синтаксис и переменные. В bash существует также help getopts, что может быть информативным.

1952
ответ дан Rob Bednark 17 August 2018 в 16:05
поделиться
  • 1
    Это правда? Согласно Wikipedia появилась новая версия расширенного GNU getopt, которая включает в себя все функции getopts, а затем некоторые. man getopt на Ubuntu 13.04 выводит getopt - parse command options (enhanced) в качестве имени, поэтому я предполагаю, что эта расширенная версия теперь стандартная. – Livven 6 June 2013 в 22:19
  • 2
    То, что что-то определенно в вашей системе, является очень слабым предпосылкой для того, чтобы основываться на предположениях «быть стандартными». на. – szablica 17 July 2013 в 16:23
  • 3
    @Livven, что getopt не является утилитой GNU, это часть util-linux. – Stephane Chazelas 20 August 2014 в 20:55
  • 4
    Если вы используете -gt 0, удалите shift после esac, добавьте все shift на 1 и добавьте этот случай: *) break;; вы можете обрабатывать необязательные аргументы. Пример: pastebin.com/6DJ57HTc – Nicolas Mongrain-Lacombe 19 June 2016 в 21:22
  • 5
    Вы не эхо –default. В первом примере я замечаю, что если –default является последним аргументом, он не обрабатывается (считается необязательным), если while [[ $# -gt 1 ]] не установлен как while [[ $# -gt 0 ]] – kolydart 10 July 2017 в 08:11

Вот как я делаю в функции, чтобы избежать прерывания работы getopts в то же время где-то выше в стеке:

function waitForWeb () {
   local OPTIND=1 OPTARG OPTION
   local host=localhost port=8080 proto=http
   while getopts "h:p:r:" OPTION; do
      case "$OPTION" in
      h)
         host="$OPTARG"
         ;;
      p)
         port="$OPTARG"
         ;;
      r)
         proto="$OPTARG"
         ;;
      esac
   done
...
}
6
ответ дан akostadinov 17 August 2018 в 16:05
поделиться

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

#!/bin/bash
#

readopt='getopts $opts opt;rc=$?;[ $rc$opt == 0? ]&&exit 1;[ $rc == 0 ]||{ shift $[OPTIND-1];false; }'

opts=vfdo:

# Enumerating options
while eval $readopt
do
    echo OPT:$opt ${OPTARG+OPTARG:$OPTARG}
done

# Enumerating arguments
for arg
do
    echo ARG:$arg
done

Пример вызова:

./myscript -v -do /fizz/someOtherFile -f ./foo/bar/someFile
OPT:v 
OPT:d 
OPT:o OPTARG:/fizz/someOtherFile
OPT:f 
ARG:./foo/bar/someFile
12
ответ дан Alek 17 August 2018 в 16:05
поделиться
  • 1
    Я читал все, и это мой любимый. Мне не нравится использовать -a=1 как стиль argc. Я предпочитаю сначала поставить основной вариант - варианты, а затем специальные с одним интервалом -o option. Я ищу простейший вариант для чтения argvs. – erm3nda 20 May 2015 в 22:50
  • 2
    Он работает очень хорошо, но если вы передадите аргумент опции «не»: все параметры будут приняты в качестве аргументов. Вы можете проверить эту строку ./myscript -v -d fail -o /fizz/someOtherFile -f ./foo/bar/someFile на свой собственный скрипт. -d не устанавливается как d: – erm3nda 20 May 2015 в 23:25

Вот мой подход - использование regexp.

  • no getopts
  • обрабатывает блок коротких параметров -qwerty
  • обрабатывает короткие параметры -q -w -e
  • обрабатывает длинные опции --qwerty
  • , вы можете передать атрибут короткому или длинному варианту (если вы используете блок коротких опций, атрибут привязан к последнему параметру )
  • вы можете использовать пробелы или = для предоставления атрибутов, но атрибут соответствует до тех пор, пока не встретит дефис + пробел «разделитель», поэтому в --q=qwe ty qwe ty есть один атрибут
  • он обрабатывает смешение всего выше, поэтому -o a -op attr ibute --option=att ribu te --op-tion attribute --option att-ribute действительно

скрипт:

#!/usr/bin/env sh

help_menu() {
  echo "Usage:

  ${0##*/} [-h][-l FILENAME][-d]

Options:

  -h, --help
    display this help and exit

  -l, --logfile=FILENAME
    filename

  -d, --debug
    enable debug
  "
}

parse_options() {
  case $opt in
    h|help)
      help_menu
      exit
     ;;
    l|logfile)
      logfile=${attr}
      ;;
    d|debug)
      debug=true
      ;;
    *)
      echo "Unknown option: ${opt}\nRun ${0##*/} -h for help.">&2
      exit 1
  esac
}
options=$@

until [ "$options" = "" ]; do
  if [[ $options =~ (^ *(--([a-zA-Z0-9-]+)|-([a-zA-Z0-9-]+))(( |=)(([\_\.\?\/\\a-zA-Z0-9]?[ -]?[\_\.\?a-zA-Z0-9]+)+))?(.*)|(.+)) ]]; then
    if [[ ${BASH_REMATCH[3]} ]]; then # for --option[=][attribute] or --option[=][attribute]
      opt=${BASH_REMATCH[3]}
      attr=${BASH_REMATCH[7]}
      options=${BASH_REMATCH[9]}
    elif [[ ${BASH_REMATCH[4]} ]]; then # for block options -qwert[=][attribute] or single short option -a[=][attribute]
      pile=${BASH_REMATCH[4]}
      while (( ${#pile} > 1 )); do
        opt=${pile:0:1}
        attr=""
        pile=${pile/${pile:0:1}/}
        parse_options
      done
      opt=$pile
      attr=${BASH_REMATCH[7]}
      options=${BASH_REMATCH[9]}
    else # leftovers that don't match
      opt=${BASH_REMATCH[10]}
      options=""
    fi
    parse_options
  fi
done
1
ответ дан a_z 17 August 2018 в 16:05
поделиться
  • 1
    Как этот. Возможно, просто добавьте -e param для эха с новой строкой. – mauron85 21 June 2017 в 06:03

В ответе нет ответа расширенный getopt . И [0] голосовой ответ вводит в заблуждение: он игнорирует короткие опции стиля -⁠vfd (запрошенные OP), параметры после позиционных аргументов (также запрашиваемые OP) и игнорируют ошибки синтаксического анализа. Вместо этого:

  • Используйте расширенный getopt из util-linux или ранее GNU glibc.1
  • Он работает с getopt_long() функцией C GNU glibc.
  • Имеет все полезные отличительные черты (другие не имеют их): обрабатывает пробелы, цитирует символы и даже двоичные в аргументах2, он может обрабатывать опции в конце: script.sh -o outFile file1 file2 -v позволяет = -style long options: script.sh --outfile=fileOut --infile fileIn
  • Уже такой старый3, что никакой системы GNU этого не хватает (например, любой Linux имеет это).
  • Вы можете проверить свое существование с помощью: getopt --test → вернуть значение 4.
  • Другие getopt или shell-builtin getopts имеют ограниченное использование.

Следующие вызовы

myscript -vfd ./foo/bar/someFile -o /fizz/someOtherFile
myscript -v -f -d -o/fizz/someOtherFile -- ./foo/bar/someFile
myscript --verbose --force --debug ./foo/bar/someFile -o/fizz/someOtherFile
myscript --output=/fizz/someOtherFile ./foo/bar/someFile -vfd
myscript ./foo/bar/someFile -df -v --output /fizz/someOtherFile

все возвращают

verbose: y, force: y, debug: y, in: ./foo/bar/someFile, out: /fizz/someOtherFile

со следующим myscript

#!/bin/bash
# saner programming env: these switches turn some bugs into errors
set -o errexit -o pipefail -o noclobber -o nounset

! getopt --test > /dev/null 
if [[ ${PIPESTATUS[0]} -ne 4 ]]; then
    echo "I’m sorry, `getopt --test` failed in this environment."
    exit 1
fi

OPTIONS=dfo:v
LONGOPTS=debug,force,output:,verbose

# -use ! and PIPESTATUS to get exit code with errexit set
# -temporarily store output to be able to check for errors
# -activate quoting/enhanced mode (e.g. by writing out “--options”)
# -pass arguments only via   -- "$@"   to separate them correctly
! PARSED=$(getopt --options=$OPTIONS --longoptions=$LONGOPTS --name "$0" -- "$@")
if [[ ${PIPESTATUS[0]} -ne 0 ]]; then
    # e.g. return value is 1
    #  then getopt has complained about wrong arguments to stdout
    exit 2
fi
# read getopt’s output this way to handle the quoting right:
eval set -- "$PARSED"

d=n f=n v=n outFile=-
# now enjoy the options in order and nicely split until we see --
while true; do
    case "$1" in
        -d|--debug)
            d=y
            shift
            ;;
        -f|--force)
            f=y
            shift
            ;;
        -v|--verbose)
            v=y
            shift
            ;;
        -o|--output)
            outFile="$2"
            shift 2
            ;;
        --)
            shift
            break
            ;;
        *)
            echo "Programming error"
            exit 3
            ;;
    esac
done

# handle non-option arguments
if [[ $# -ne 1 ]]; then
    echo "$0: A single input file is required."
    exit 4
fi

echo "verbose: $v, force: $f, debug: $d, in: $1, out: $outFile"

1 расширенный getopt доступен на большинстве «bash-систем», включая Cygwin; на OS X попробуйте brew install gnu-getopt или sudo port install getopt 2 соглашения POSIX exec() не имеют надежного способа передать двоичный NULL в аргументы командной строки; эти байты преждевременно заканчивают первую версию аргумента 3, выпущенную в 1997 году или ранее (я только отследил ее до 1997 года)

337
ответ дан BallpointBen 17 August 2018 в 16:05
поделиться
  • 1
    Спасибо за это. Просто подтвердите из таблицы функций в ru.wikipedia.org/wiki/Getopts , если вам нужна поддержка длинных параметров, а вы не в Solaris, getopt - это путь. – johncip 12 January 2017 в 03:00
  • 2
    Я считаю, что единственное предостережение с getopt заключается в том, что его нельзя использовать удобно в сценариях оболочки, где у него может быть несколько параметров, специфичных для сценария оболочки, а затем передать параметры не-оболочки-сценария для завернутый исполняемый файл, неповрежденный. Предположим, у меня есть обертка grep с именем mygrep, и у меня есть опция --foo, характерная для mygrep, тогда я не могу сделать mygrep --foo -A 2, и -A 2 автоматически передается grep; I необходимо выполнить mygrep --foo -- -A 2. Здесь моя реализация поверх вашего решения. – Kaushal Modi 27 April 2017 в 14:02
  • 3
    @bobpaul Ваше утверждение о util-linux неверно и вводит в заблуждение: пакет отмечен как «существенный» на Ubuntu / Debian. Таким образом, он всегда устанавливается. - О каких дистрибутивах вы говорите (где, как вы говорите, его необходимо установить специально)? – Robert Siemer 21 March 2018 в 10:16
  • 4
    @Jason, getopt не будет «для вас». Не нужно проверять это самостоятельно. – Robert Siemer 4 May 2018 в 08:22
  • 5
    @AlexYursha @Marcos @bobpaul Улучшенный скрипт теперь использует errexit aka set -e довольно элегантно. – Robert Siemer 10 July 2018 в 22:08

С риском добавления другого примера для игнорирования, вот моя схема.

  • handle -n arg и --name=arg
  • разрешает аргументы в конце
  • показывает правильные ошибки, если что-то с ошибкой
  • совместимо, не использует bashisms
  • , читается, не требует сохранения состояния в цикле

Надеюсь, что это кому-то полезно.

while [ "$#" -gt 0 ]; do
  case "$1" in
    -n) name="$2"; shift 2;;
    -p) pidfile="$2"; shift 2;;
    -l) logfile="$2"; shift 2;;

    --name=*) name="${1#*=}"; shift 1;;
    --pidfile=*) pidfile="${1#*=}"; shift 1;;
    --logfile=*) logfile="${1#*=}"; shift 1;;
    --name|--pidfile|--logfile) echo "$1 requires an argument" >&2; exit 1;;

    -*) echo "unknown option: $1" >&2; exit 1;;
    *) handle_argument "$1"; shift 1;;
  esac
done
63
ответ дан bronson 17 August 2018 в 16:05
поделиться
  • 1
    Что такое & quot; handle_argument & quot; функционировать? – rhombidodecahedron 11 September 2015 в 08:40
  • 2
    Извините за задержку. В моем скрипте функция handle_argument получает все необязательные аргументы. Вы можете заменить эту строку тем, что хотите, возможно, *) die "unrecognized argument: $1" или собрать args в переменную *) args+="$1"; shift 1;;. – bronson 8 October 2015 в 20:41
  • 3
    Удивительно! Я проверил пару ответов, но это единственный, который работал во всех случаях, включая многие позиционные параметры (как до, так и после флагов) – Guilherme Garnier 13 April 2018 в 16:10
  • 4
    – Jens Nedregård 21 October 2018 в 13:23

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

https://argbash.io

21
ответ дан bubla 17 August 2018 в 16:05
поделиться
  • 1
    Спасибо, что написал argbash, я просто использовал его и нашел, что он работает хорошо. В основном я пошел на argbash, потому что это генератор кода, поддерживающий более старый bash 3.x, найденный на OS X 10.11 El Capitan. Единственным недостатком является то, что подход с генератором кода означает довольно много кода в вашем главном скрипте по сравнению с вызовом модуля. – RichVel 18 August 2016 в 05:34
  • 2
    Вы можете фактически использовать Argbash таким образом, чтобы он создавал специально разработанную библиотеку синтаксического анализа только для вас, которую вы могли бы включить в свой скрипт, или вы можете иметь его в отдельном файле и просто использовать его. Я добавил пример , чтобы продемонстрировать это, и я сделал его более явным в документации. – bubla 23 August 2016 в 20:40
  • 3
    Хорошо знать. Этот пример интересен, но все еще не совсем ясен - возможно, вы можете изменить имя сгенерированного скрипта на «parse_lib.sh» или подобное и показать, где его вызывает главный сценарий (например, в разделе сценария wrapper, который является более сложным вариантом использования). – RichVel 24 August 2016 в 05:47
  • 4
    Проблемы были рассмотрены в последней версии argbash: была улучшена документация, был введен сценарий quickstart argbash-init, и вы даже можете использовать argbash онлайн в argbash.io/generate – bubla 2 December 2016 в 21:12

Другое решение без getopt [s], POSIX, старый стиль Unix

Как и в , решение Bruno Bronosky опубликовано , здесь это одно без использования getopt(s).

Основная отличительная особенность моего решения заключается в том, что он позволяет объединять параметры, как tar -xzf foo.tar.gz, равно tar -x -z -f foo.tar.gz. И так же, как в tar, ps и т. Д. Ведущий дефис является необязательным для блока коротких опций (но это можно легко изменить).

Код с примерами опций

#!/bin/sh

echo
echo "POSIX-compliant getopt(s)-free old-style-supporting option parser from phk@[se.unix]"
echo

print_usage() {
  echo "Usage:

  $0 {a|b|c} [ARG...]

Options:

  --aaa-0-args
  -a
    Option without arguments.

  --bbb-1-args ARG
  -b ARG
    Option with one argument.

  --ccc-2-args ARG1 ARG2
  -c ARG1 ARG2
    Option with two arguments.

" >&2
}

if [ $# -le 0 ]; then
  print_usage
  exit 1
fi

opt=
while :; do

  if [ $# -le 0 ]; then

    # no parameters remaining -> end option parsing
    break

  elif [ ! "$opt" ]; then

    # we are at the beginning of a fresh block
    # remove optional leading hyphen and strip trailing whitespaces
    opt=$(echo "$1" | sed 's/^-\?\([a-zA-Z0-9\?-]*\)/\1/')

  fi

  # get the first character -> check whether long option
  first_chr=$(echo "$opt" | awk '{print substr($1, 1, 1)}')
  [ "$first_chr" = - ] && long_option=T || long_option=F

  # note to write the options here with a leading hyphen less
  # also do not forget to end short options with a star
  case $opt in

    -)

      # end of options
      shift
      break
      ;;

    a*|-aaa-0-args)

      echo "Option AAA activated!"
      ;;

    b*|-bbb-1-args)

      if [ "$2" ]; then
        echo "Option BBB with argument '$2' activated!"
        shift
      else
        echo "BBB parameters incomplete!" >&2
        print_usage
        exit 1
      fi
      ;;

    c*|-ccc-2-args)

      if [ "$2" ] && [ "$3" ]; then
        echo "Option CCC with arguments '$2' and '$3' activated!"
        shift 2
      else
        echo "CCC parameters incomplete!" >&2
        print_usage
        exit 1
      fi
      ;;

    h*|\?*|-help)

      print_usage
      exit 0
      ;;

    *)

      if [ "$long_option" = T ]; then
        opt=$(echo "$opt" | awk '{print substr($1, 2)}')
      else
        opt=$first_chr
      fi
      printf 'Error: Unknown option: "%s"\n' "$opt" >&2
      print_usage
      exit 1
      ;;

  esac

  if [ "$long_option" = T ]; then

    # if we had a long option then we are going to get a new block next
    shift
    opt=

  else

    # if we had a short option then just move to the next character
    opt=$(echo "$opt" | awk '{print substr($1, 2)}')

    # if block is now empty then shift to the next one
    [ "$opt" ] || shift

  fi

done

echo "Doing something..."

exit 0

Для примера использования см. Раздел «Дополнительные параметры». примеры ниже.

Позиция опций с аргументами

Для чего его ценность там опционы с аргументами не являются последними (должны быть только длинные варианты). Таким образом, пока, например, в tar (по крайней мере, в некоторых реализациях) параметры f должны быть последними, потому что имя файла следует (tar xzf bar.tar.gz работает, но tar xfz bar.tar.gz не делает) это не так (см. более поздние примеры).

Несколько опций с аргументами

В качестве еще одного бонуса параметры параметра потребляются в порядке параметров по параметрам с требуемыми параметрами. Просто посмотрите на вывод моего скрипта здесь с помощью командной строки abc X Y Z (или -abc X Y Z):

Option AAA activated!
Option BBB with argument 'X' activated!
Option CCC with arguments 'Y' and 'Z' activated!

Длинные опции также объединены

Также вы также можете длинные опции в блоке опций, учитывая, что они встречаются последними в блоке. Таким образом, следующие командные строки эквивалентны (включая порядок, в котором обрабатываются параметры и его аргументы):

  • -cba Z Y X
  • cba Z Y X
  • -cb-aaa-0-args Z Y X
  • -c-bbb-1-args Z Y X -a
  • --ccc-2-args Z Y -ba X
  • c Z Y b X a
  • -c Z Y -b X -a
  • --ccc-2-args Z Y --bbb-1-args X --aaa-0-args

Все это приводит к:

Option CCC with arguments 'Z' and 'Y' activated!
Option BBB with argument 'X' activated!
Option AAA activated!
Doing something...

Не в этом решении

Дополнительные аргументы

Параметры с дополнительными аргументами должны быть возможны с небольшим количеством работы, например глядя вперед, есть ли блок без дефиса; пользователь должен будет поместить дефис перед каждым блоком после блока с параметром, имеющим необязательный параметр. Возможно, это слишком сложно для общения с пользователем, поэтому лучше всего в этом случае просто взять ведущий дефис.

Все становится еще сложнее с несколькими возможными параметрами. Я бы посоветовал не делать параметры, пытаясь быть умными, определяя, может ли для него аргумент или нет (например, с опцией просто принимает число как необязательный аргумент), потому что это может сломаться в будущем.

Я предпочитаю дополнительные опции вместо необязательных аргументов.

Опционные аргументы, введенные с знаком равенства

Как и с необязательными аргументами, я не поклонник этого (BTW, есть ли там нить для обсуждения плюсов и минусов разных стилей параметров?), но если вы этого хотите, вы могли бы реализовать его сами, как это сделано в http://mywiki.wooledge.org/BashFAQ/035#Manual_loop с оператором case --long-with-arg=?*, а затем снятие знака равенства (это BTW сайт, в котором говорится, что создание конкатенации параметров возможно с некоторыми усилиями, но «оставило [это] как упражнение для читателя», что заставило меня взять их на их слово, но я начал с нуля).

Другие примечания

POSIX-совместимый, работает даже на ancie nt Настройка Busybox, с которой мне приходилось иметь дело (например, cut, head и getopts отсутствуют.

21
ответ дан Community 17 August 2018 в 16:05
поделиться

Главный ответ на этот вопрос казался немного ошибкой, когда я его попробовал - вот мое решение, которое я считаю более надежным:

boolean_arg=""
arg_with_value=""

while [[ $# -gt 0 ]]
do
key="$1"
case $key in
    -b|--boolean-arg)
    boolean_arg=true
    shift
    ;;
    -a|--arg-with-value)
    arg_with_value="$2"
    shift
    shift
    ;;
    -*)
    echo "Unknown option: $1"
    exit 1
    ;;
    *)
    arg_num=$(( $arg_num + 1 ))
    case $arg_num in
        1)
        first_normal_arg="$1"
        shift
        ;;
        2)
        second_normal_arg="$1"
        shift
        ;;
        *)
        bad_args=TRUE
    esac
    ;;
esac
done

# Handy to have this here when adding arguments to
# see if they're working. Just edit the '0' to be '1'.
if [[ 0 == 1 ]]; then
    echo "first_normal_arg: $first_normal_arg"
    echo "second_normal_arg: $second_normal_arg"
    echo "boolean_arg: $boolean_arg"
    echo "arg_with_value: $arg_with_value"
    exit 0
fi

if [[ $bad_args == TRUE || $arg_num < 2 ]]; then
    echo "Usage: $(basename "$0") <first-normal-arg> <second-normal-arg> [--boolean-arg] [--arg-with-value VALUE]"
    exit 1
fi
1
ответ дан Daniel Bigham 17 August 2018 в 16:05
поделиться

Я написал помощника bash, чтобы написать хороший инструмент bash

project home: https://gitlab.mbedsys.org/mbedsys/bashopts

:

#!/bin/bash -ei

# load the library
. bashopts.sh

# Enable backtrace dusplay on error
trap 'bashopts_exit_handle' ERR

# Initialize the library
bashopts_setup -n "$0" -d "This is myapp tool description displayed on help message" -s "$HOME/.config/myapprc"

# Declare the options
bashopts_declare -n first_name -l first -o f -d "First name" -t string -i -s -r
bashopts_declare -n last_name -l last -o l -d "Last name" -t string -i -s -r
bashopts_declare -n display_name -l display-name -t string -d "Display name" -e "\$first_name \$last_name"
bashopts_declare -n age -l number -d "Age" -t number
bashopts_declare -n email_list -t string -m add -l email -d "Email adress"

# Parse arguments
bashopts_parse_args "$@"

# Process argument
bashopts_process_args

поможет:

NAME:
    ./example.sh - This is myapp tool description displayed on help message

USAGE:
    [options and commands] [-- [extra args]]

OPTIONS:
    -h,--help                          Display this help
    -n,--non-interactive true          Non interactive mode - [$bashopts_non_interactive] (type:boolean, default:false)
    -f,--first "John"                  First name - [$first_name] (type:string, default:"")
    -l,--last "Smith"                  Last name - [$last_name] (type:string, default:"")
    --display-name "John Smith"        Display name - [$display_name] (type:string, default:"$first_name $last_name")
    --number 0                         Age - [$age] (type:number, default:0)
    --email                            Email adress - [$email_list] (type:string, default:"")

пользоваться:)

1
ответ дан Emeric Verschuur 17 August 2018 в 16:05
поделиться
  • 1
    Я получаю это в Mac OS X: `` `lib / bashopts.sh: строка 138: declare: -A: недопустимая опция declare: use: declare [-afFirtx] [-p] [name [= value] ...] Ошибка в lib / bashopts.sh: 138. 'declare -x -A bashopts_optprop_name' выходит из состояния 2 Дерево вызовов: 1: lib / controller.sh: 4 source (...) Выход со статусом 1 `` ` – Josh Wulf 24 June 2017 в 18:07
  • 2
    Для этого вам нужна версия Bash 4. На Mac версия по умолчанию - 3. Вы можете использовать домашний варочный инструмент для установки bash 4. – Josh Wulf 24 June 2017 в 18:17

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

-s p1
--stage p1
-w somefolder
--workfolder somefolder
-sw p1 somefolder
-e=hello

Также допускает это (может быть нежелательно):

-s--workfolder p1 somefolder
-se=hello p1
-swe=hello p1 somefolder

Вы должны решить перед тем, как использовать if = для опции или нет. Это значит, что код чист (ish).

while [[ $# > 0 ]]
do
    key="$1"
    while [[ ${key+x} ]]
    do
        case $key in
            -s*|--stage)
                STAGE="$2"
                shift # option has parameter
                ;;
            -w*|--workfolder)
                workfolder="$2"
                shift # option has parameter
                ;;
            -e=*)
                EXAMPLE="${key#*=}"
                break # option has been fully handled
                ;;
            *)
                # unknown option
                echo Unknown option: $key #1>&2
                exit 10 # either this: my preferred way to handle unknown options
                break # or this: do this to signal the option has been handled (if exit isn't used)
                ;;
        esac
        # prepare for next option in this key, if any
        [[ "$key" = -? || "$key" == --* ]] && unset key || key="${key/#-?/-}"
    done
    shift # option(s) fully processed, proceed to next input argument
done
3
ответ дан galmok 17 August 2018 в 16:05
поделиться
  • 1
    в чем смысл «+ x». на $ {key + x}? – Luca Davanzo 14 November 2016 в 18:56
  • 2
    Это тест, чтобы увидеть, присутствует ли «ключ» или нет. Далее вниз я отключаю ключ, и это прерывает внутренний цикл while. – galmok 15 November 2016 в 10:10

from: digitalpeer.com с незначительными модификациями

Использование myscript.sh -p=my_prefix -s=dirname -l=libname

#!/bin/bash
for i in "$@"
do
case $i in
    -p=*|--prefix=*)
    PREFIX="${i#*=}"

    ;;
    -s=*|--searchpath=*)
    SEARCHPATH="${i#*=}"
    ;;
    -l=*|--lib=*)
    DIR="${i#*=}"
    ;;
    --default)
    DEFAULT=YES
    ;;
    *)
            # unknown option
    ;;
esac
done
echo PREFIX = ${PREFIX}
echo SEARCH PATH = ${SEARCHPATH}
echo DIRS = ${DIR}
echo DEFAULT = ${DEFAULT}

Чтобы лучше понять ${i#*=} поиск «Удаление подстроки "в этом руководстве . Он функционально эквивалентен `sed 's/[^=]*=//' <<< "$i"`, который вызывает ненужный подпроцесс или `echo "$i" | sed 's/[^=]*=//'`, который вызывает два ненужных подпроцесса.

104
ответ дан guneysus 17 August 2018 в 16:05
поделиться
  • 1
    Ухоженная! Хотя это не будет работать для разделенных пробелом аргументов à la mount -t tempfs .... Вероятно, можно исправить это через что-то вроде while [ $# -ge 1 ]; do param=$1; shift; case $param in; -p) prefix=$1; shift;; и т. Д. – Tobias Kienzler 12 November 2013 в 14:48
  • 2
    Это не может сочетать короткие сочетания стиля -vfd. – Robert Siemer 19 March 2016 в 16:23
  • 3
    ссылка сломана! – bekur 20 December 2017 в 00:27

getopts отлично работает, если # 1 у вас он установлен, а # 2 вы собираетесь запускать его на той же платформе. OSX и Linux (например) ведут себя по-другому в этом отношении.

Вот решение (не getopts), которое поддерживает равные, неравные и булевские флаги. Например, вы можете запустить свой скрипт таким образом:

./script --arg1=value1 --arg2 value2 --shouldClean

# parse the arguments.
COUNTER=0
ARGS=("$@")
while [ $COUNTER -lt $# ]
do
    arg=${ARGS[$COUNTER]}
    let COUNTER=COUNTER+1
    nextArg=${ARGS[$COUNTER]}

    if [[ $skipNext -eq 1 ]]; then
        echo "Skipping"
        skipNext=0
        continue
    fi

    argKey=""
    argVal=""
    if [[ "$arg" =~ ^\- ]]; then
        # if the format is: -key=value
        if [[ "$arg" =~ \= ]]; then
            argVal=$(echo "$arg" | cut -d'=' -f2)
            argKey=$(echo "$arg" | cut -d'=' -f1)
            skipNext=0

        # if the format is: -key value
        elif [[ ! "$nextArg" =~ ^\- ]]; then
            argKey="$arg"
            argVal="$nextArg"
            skipNext=1

        # if the format is: -key (a boolean flag)
        elif [[ "$nextArg" =~ ^\- ]] || [[ -z "$nextArg" ]]; then
            argKey="$arg"
            argVal=""
            skipNext=0
        fi
    # if the format has not flag, just a value.
    else
        argKey=""
        argVal="$arg"
        skipNext=0
    fi

    case "$argKey" in 
        --source-scmurl)
            SOURCE_URL="$argVal"
        ;;
        --dest-scmurl)
            DEST_URL="$argVal"
        ;;
        --version-num)
            VERSION_NUM="$argVal"
        ;;
        -c|--clean)
            CLEAN_BEFORE_START="1"
        ;;
        -h|--help|-help|--h)
            showUsage
            exit
        ;;
    esac
done
7
ответ дан Hive 17 August 2018 в 16:05
поделиться

Более сжатый способ

script.sh

#!/bin/bash

while [[ "$#" > 0 ]]; do case $1 in
  -d|--deploy) deploy="$2"; shift;;
  -u|--uglify) uglify=1;;
  *) echo "Unknown parameter passed: $1"; exit 1;;
esac; shift; done

echo "Should deploy? $deploy"
echo "Should uglify? $uglify"

Использование:

./script.sh -d dev -u

# OR:

./script.sh --deploy dev --uglify
41
ответ дан Inanc Gumus 17 August 2018 в 16:05
поделиться

Предположим, что мы создаем сценарий оболочки с именем test_args.sh следующим образом

#!/bin/sh
until [ $# -eq 0 ]
do
  name=${1:1}; shift;
  if [[ -z "$1" || $1 == -* ]] ; then eval "export $name=true"; else eval "export $name=$1"; shift; fi  
done
echo "year=$year month=$month day=$day flag=$flag"

После выполнения следующей команды:

sh test_args.sh  -year 2017 -flag  -month 12 -day 22 

Выход будет:

year=2017 month=12 day=22 flag=true
2
ответ дан John 17 August 2018 в 16:05
поделиться
  • 1
    Это использует тот же подход, что и ответ Ноя , но имеет меньше проверок безопасности / гарантий. Это позволяет нам записывать произвольные аргументы в среду сценария, и я уверен, что использование вами eval может позволить команду впрыска. – Will Barnwell 10 October 2017 в 23:57

Смешивание позиционных и флаговых аргументов

- param = arg (равно разграничено)

Свободно смешивающие флажки между позиционными аргументами:

./script.sh dumbo 127.0.0.1 --environment=production -q -d
./script.sh dumbo --environment=production 127.0.0.1 --quiet -d

может выполняться с довольно кратким подходом:

# process flags
pointer=1
while [[ $pointer -le $# ]]; do
   param=${!pointer}
   if [[ $param != "-"* ]]; then ((pointer++)) # not a parameter flag so advance pointer
   else
      case $param in
         # paramter-flags with arguments
         -e=*|--environment=*) environment="${param#*=}";;
                  --another=*) another="${param#*=}";;

         # binary flags
         -q|--quiet) quiet=true;;
                 -d) debug=true;;
      esac

      # splice out pointer frame from positional list
      [[ $pointer -gt 1 ]] \
         && set -- ${@:1:((pointer - 1))} ${@:((pointer + 1)):$#} \
         || set -- ${@:((pointer + 1)):$#};
   fi
done

# positional remain
node_name=$1
ip_address=$2

- param arg (пробел)

Обычно проще не смешивать стили --flag=value и --flag value.

./script.sh dumbo 127.0.0.1 --environment production -q -d

Это немного похоже на чтение, но все еще актуально

./script.sh dumbo --environment production 127.0.0.1 --quiet -d

Источник

# process flags
pointer=1
while [[ $pointer -le $# ]]; do
   if [[ ${!pointer} != "-"* ]]; then ((pointer++)) # not a parameter flag so advance pointer
   else
      param=${!pointer}
      ((pointer_plus = pointer + 1))
      slice_len=1

      case $param in
         # paramter-flags with arguments
         -e|--environment) environment=${!pointer_plus}; ((slice_len++));;
                --another) another=${!pointer_plus}; ((slice_len++));;

         # binary flags
         -q|--quiet) quiet=true;;
                 -d) debug=true;;
      esac

      # splice out pointer frame from positional list
      [[ $pointer -gt 1 ]] \
         && set -- ${@:1:((pointer - 1))} ${@:((pointer + $slice_len)):$#} \
         || set -- ${@:((pointer + $slice_len)):$#};
   fi
done

# positional remain
node_name=$1
ip_address=$2
2
ответ дан Mark Fox 17 August 2018 в 16:05
поделиться

Вот мое улучшенное решение ответа Бруно Броноски с использованием переменных массивов.

позволяет вам смешивать положение параметров и давать вам массив параметров, сохраняющий порядок без опций

#!/bin/bash

echo $@

PARAMS=()
SOFT=0
SKIP=()
for i in "$@"
do
case $i in
    -n=*|--skip=*)
    SKIP+=("${i#*=}")
    ;;
    -s|--soft)
    SOFT=1
    ;;
    *)
        # unknown option
        PARAMS+=("$i")
    ;;
esac
done
echo "SKIP            = ${SKIP[@]}"
echo "SOFT            = $SOFT"
    echo "Parameters:"
    echo ${PARAMS[@]}

Будет выводиться, например:

$ ./test.sh parameter -s somefile --skip=.c --skip=.obj
parameter -s somefile --skip=.c --skip=.obj
SKIP            = .c .obj
SOFT            = 1
Parameters:
parameter somefile
1
ответ дан Masadow 17 August 2018 в 16:05
поделиться
  • 1
    Вы используете сдвиг на известных аргументах, а не на неизвестных, поэтому ваш оставшийся $@ будет содержать все два первых аргумента (в том порядке, в котором они передаются), что может привести к некоторым ошибкам, если вы попытаетесь использовать $@ позже. Вам не нужен сдвиг для параметров =, поскольку вы не обрабатываете пробелы, и вы получаете значение с удалением подстроки #*= – Jason S 3 December 2017 в 02:01
  • 2
    Вы правы, на самом деле, так как я создаю переменную PARAMS, мне не нужно использовать сдвиг вообще – Masadow 5 December 2017 в 10:17

getopt() / getopts() является хорошим вариантом. здесь :

Простое использование «getopt» показано в этом мини-скрипте:

#!/bin/bash
echo "Before getopt"
for i
do
  echo $i
done
args=`getopt abc:d $*`
set -- $args
echo "After getopt"
for i
do
  echo "-->$i"
done

Мы сказали, что любой из -a, -b, -c или -d будет разрешен, но за -c следует аргумент (говорит «c:»).

Если мы назовем это «g» и попробуем:

bash-2.05a$ ./g -abc foo
Before getopt
-abc
foo
After getopt
-->-a
-->-b
-->-c
-->foo
-->--

Начнем с двух аргументов, а «getopt» разрывает опции и помещает их в свои собственный аргумент. Он также добавил «-».

101
ответ дан Matt J 17 August 2018 в 16:05
поделиться
  • 1
    Использование $* прерывается использованием getopt. (Он использует аргументы с пробелами.) Для правильного использования см. мой ответ . – Robert Siemer 16 April 2016 в 14:37
  • 2
    Почему вы хотите сделать это более сложным? – SDsolar 10 August 2017 в 14:07
  • 3
    @Matt J, первая часть скрипта (для i) сможет обрабатывать аргументы с пробелами в них, если вы используете & quot; $ i & quot; вместо $ i. Кажется, что getopts не может обрабатывать аргументы с пробелами. Каким будет преимущество использования getopt для цикла for i? – thebunnyrules 1 June 2018 в 01:57

Это также может быть полезно знать, вы можете установить значение, и если кто-то предоставит ввод, переопределите значение по умолчанию с этим значением.

myscript.sh -f ./serverlist.txt или просто. /myscript.sh (и он принимает значения по умолчанию)

    #!/bin/bash
    # --- set the value, if there is inputs, override the defaults.

    HOME_FOLDER="${HOME}/owned_id_checker"
    SERVER_FILE_LIST="${HOME_FOLDER}/server_list.txt"

    while [[ $# > 1 ]]
    do
    key="$1"
    shift

    case $key in
        -i|--inputlist)
        SERVER_FILE_LIST="$1"
        shift
        ;;
    esac
    done


    echo "SERVER LIST   = ${SERVER_FILE_LIST}"
1
ответ дан Mike Q 17 August 2018 в 16:05
поделиться

Решение, которое сохраняет необработанные аргументы. Демонстрации включены.

Вот мое решение. Это ОЧЕНЬ гибкий и, в отличие от других, не требует внешних пакетов и чисто обрабатывает оставшиеся аргументы.

Использование: ./myscript -flag flagvariable -otherflag flagvar2

Все, что вам нужно сделать, это отредактировать строку validflags , Он добавляет дефис и ищет все аргументы. Затем он определяет следующий аргумент как имя флага, например

./myscript -flag flagvariable -otherflag flagvar2
echo $flag $otherflag
flagvariable flagvar2

Основной код (короткая версия, подробный с примерами ниже, также версия с ошибкой):

#!/usr/bin/env bash
#shebang.io
validflags="rate time number"
count=1
for arg in $@
do
    match=0
    argval=$1
    for flag in $validflags
    do
        sflag="-"$flag
        if [ "$argval" == "$sflag" ]
        then
            declare $flag=$2
            match=1
        fi
    done
        if [ "$match" == "1" ]
    then
        shift 2
    else
        leftovers=$(echo $leftovers $argval)
        shift
    fi
    count=$(($count+1))
done
#Cleanup then restore the leftovers
shift $#
set -- $leftovers

Вербальная версия со встроенными эхо-демонстрациями:

#!/usr/bin/env bash
#shebang.io
rate=30
time=30
number=30
echo "all args
$@"
validflags="rate time number"
count=1
for arg in $@
do
    match=0
    argval=$1
#   argval=$(echo $@ | cut -d ' ' -f$count)
    for flag in $validflags
    do
            sflag="-"$flag
        if [ "$argval" == "$sflag" ]
        then
            declare $flag=$2
            match=1
        fi
    done
        if [ "$match" == "1" ]
    then
        shift 2
    else
        leftovers=$(echo $leftovers $argval)
        shift
    fi
    count=$(($count+1))
done

#Cleanup then restore the leftovers
echo "pre final clear args:
$@"
shift $#
echo "post final clear args:
$@"
set -- $leftovers
echo "all post set args:
$@"
echo arg1: $1 arg2: $2

echo leftovers: $leftovers
echo rate $rate time $time number $number

Финальная, эта ошибка устраняется, если передан недопустимый -аргумент.

#!/usr/bin/env bash
#shebang.io
rate=30
time=30
number=30
validflags="rate time number"
count=1
for arg in $@
do
    argval=$1
    match=0
        if [ "${argval:0:1}" == "-" ]
    then
        for flag in $validflags
        do
                sflag="-"$flag
            if [ "$argval" == "$sflag" ]
            then
                declare $flag=$2
                match=1
            fi
        done
        if [ "$match" == "0" ]
        then
            echo "Bad argument: $argval"
            exit 1
        fi
        shift 2
    else
        leftovers=$(echo $leftovers $argval)
        shift
    fi
    count=$(($count+1))
done
#Cleanup then restore the leftovers
shift $#
set -- $leftovers
echo rate $rate time $time number $number
echo leftovers: $leftovers

Плюсы: Что он делает, он обрабатывает очень хорошо. Он сохраняет неиспользуемые аргументы, которые многие другие решения здесь нет. Он также позволяет вызывать переменные без указания вручную в скрипте. Он также позволяет предустановить переменные, если не указан соответствующий аргумент. (См. Подробный пример).

Минусы: не удается разобрать одну сложную строку arg, например. -xcvf будет обрабатываться как один аргумент. Вы могли бы легко написать дополнительный код в мой, который добавляет эту функциональность.

1
ответ дан Noah 17 August 2018 в 16:05
поделиться

Я даю вам функцию parse_params, которая будет анализировать параметры из командной строки.

  1. Это чистое решение Bash, никаких дополнительных утилит.
  2. загрязняют глобальный масштаб.
  3. Легко возвращает вам простые в использовании переменные, на которые вы могли бы построить дальнейшую логику.
  4. Количество тире перед параметрами не имеет значения (--all равно -all равно all=all )

Нижеприведенный сценарий представляет собой демонстрацию рабочей копии для копирования. См. Функцию show_use, чтобы понять, как использовать parse_params.

Ограничения:

  1. Не поддерживает параметры с ограничением пробела (-d 1)
  2. Имена параметров теряют тире, поэтому --any-param и -anyparam эквивалентны
  3. eval $(parse_params "$@") должны использоваться внутри функции bash (она не будет работать в глобальной области)

#!/bin/bash

# Universal Bash parameter parsing
# Parses equal sign separated params into local variables (--name=bob creates variable $name=="bob")
# Standalone named parameter value will equal its param name (--force creates variable $force=="force")
# Parses multi-valued named params into an array (--path=path1 --path=path2 creates ${path[*]} array)
# Parses un-named params into ${ARGV[*]} array
# Additionally puts all named params raw into ${ARGN[*]} array
# Additionally puts all standalone "option" params raw into ${ARGO[*]} array
# @author Oleksii Chekulaiev
# @version v1.4 (Jun-26-2018)
parse_params ()
{
    local existing_named
    local ARGV=() # un-named params
    local ARGN=() # named params
    local ARGO=() # options (--params)
    echo "local ARGV=(); local ARGN=(); local ARGO=();"
    while [[ "$1" != "" ]]; do
        # Escape asterisk to prevent bash asterisk expansion
        _escaped=${1/\*/\'\"*\"\'}
        # If equals delimited named parameter
        if [[ "$1" =~ ^..*=..* ]]; then
            # Add to named parameters array
            echo "ARGN+=('$_escaped');"
            # key is part before first =
            local _key=$(echo "$1" | cut -d = -f 1)
            # val is everything after key and = (protect from param==value error)
            local _val="${1/$_key=}"
            # remove dashes from key name
            _key=${_key//\-}
            # skip when key is empty
            if [[ "$_key" == "" ]]; then
                shift
                continue
            fi
            # search for existing parameter name
            if (echo "$existing_named" | grep "\b$_key\b" >/dev/null); then
                # if name already exists then it's a multi-value named parameter
                # re-declare it as an array if needed
                if ! (declare -p _key 2> /dev/null | grep -q 'declare \-a'); then
                    echo "$_key=(\"\$$_key\");"
                fi
                # append new value
                echo "$_key+=('$_val');"
            else
                # single-value named parameter
                echo "local $_key=\"$_val\";"
                existing_named=" $_key"
            fi
        # If standalone named parameter
        elif [[ "$1" =~ ^\-. ]]; then
            # remove dashes
            local _key=${1//\-}
            # skip when key is empty
            if [[ "$_key" == "" ]]; then
                shift
                continue
            fi
            # Add to options array
            echo "ARGO+=('$_escaped');"
            echo "local $_key=\"$_key\";"
        # non-named parameter
        else
            # Escape asterisk to prevent bash asterisk expansion
            _escaped=${1/\*/\'\"*\"\'}
            echo "ARGV+=('$_escaped');"
        fi
        shift
    done
}

#--------------------------- DEMO OF THE USAGE -------------------------------

show_use ()
{
    eval $(parse_params "$@")
    # --
    echo "${ARGV[0]}" # print first unnamed param
    echo "${ARGV[1]}" # print second unnamed param
    echo "${ARGN[0]}" # print first named param
    echo "${ARG0[0]}" # print first option param (--force)
    echo "$anyparam"  # print --anyparam value
    echo "$k"         # print k=5 value
    echo "${multivalue[0]}" # print first value of multi-value
    echo "${multivalue[1]}" # print second value of multi-value
    [[ "$force" == "force" ]] && echo "\$force is set so let the force be with you"
}

show_use "param 1" --anyparam="my value" param2 k=5 --force --multi-value=test1 --multi-value=test2
6
ответ дан Oleksii Chekulaiev 17 August 2018 в 16:05
поделиться
  • 1
    Чтобы использовать демо для синтаксического анализа параметров, которые входят в ваш сценарий bash, вы просто выполняете show_use "$@" – Oleksii Chekulaiev 28 September 2016 в 12:55
  • 2
    В основном я обнаружил, что github.com/renatosilva/easyoptions делает то же самое, но немного более массивнее, чем эта функция. – Oleksii Chekulaiev 28 September 2016 в 12:58

В этом примере показано, как использовать getopt и eval и HEREDOC и shift для обработки коротких и длинных параметров с последующим требуемым значением и без него.

#!/usr/bin/env bash

# usage function
function usage()
{
   cat << HEREDOC

   Usage: $progname [--num NUM] [--time TIME_STR] [--verbose] [--dry-run]

   optional arguments:
     -h, --help           show this help message and exit
     -n, --num NUM        pass in a number
     -t, --time TIME_STR  pass in a time string
     -v, --verbose        increase the verbosity of the bash script
     --dry-run            do a dry run, don't change any files

HEREDOC
}  

# initialize variables
progname=$(basename $0)
verbose=0
dryrun=0
num_str=
time_str=

# use getopt and store the output into $OPTS
# note the use of -o for the short options, --long for the long name options
# and a : for any option that takes a parameter
OPTS=$(getopt -o "hn:t:v" --long "help,num:,time:,verbose,dry-run" -n "$progname" -- "$@")
if [ $? != 0 ] ; then echo "Error in command line arguments." >&2 ; usage; exit 1 ; fi
eval set -- "$OPTS"

while true; do
  # uncomment the next line to see how shift is working
  # echo "\$1:\"$1\" \$2:\"$2\""
  case "$1" in
    -h | --help ) usage; exit; ;;
    -n | --num ) num_str="$2"; shift 2 ;;
    -t | --time ) time_str="$2"; shift 2 ;;
    --dry-run ) dryrun=1; shift ;;
    -v | --verbose ) verbose=$((verbose + 1)); shift ;;
    -- ) shift; break ;;
    * ) break ;;
  esac
done

if (( $verbose > 0 )); then

   # print out all the parameters we read in
   cat <<-EOM
   num=$num_str
   time=$time_str
   verbose=$verbose
   dryrun=$dryrun
EOM
fi

# The rest of your script below

Наиболее значимые строки сценария выше:

OPTS=$(getopt -o "hn:t:v" --long "help,num:,time:,verbose,dry-run" -n "$progname" -- "$@")
if [ $? != 0 ] ; then echo "Error in command line arguments." >&2 ; exit 1 ; fi
eval set -- "$OPTS"

while true; do
  case "$1" in
    -h | --help ) usage; exit; ;;
    -n | --num ) num_str="$2"; shift 2 ;;
    -t | --time ) time_str="$2"; shift 2 ;;
    --dry-run ) dryrun=1; shift ;;
    -v | --verbose ) verbose=$((verbose + 1)); shift ;;
    -- ) shift; break ;;
    * ) break ;;
  esac
done

Короче говоря, точка, читаем и обрабатываем практически все (ИМХО).

Надеюсь, что это поможет кому-то.

1
ответ дан phyatt 17 August 2018 в 16:05
поделиться

EasyOptions не требует никакого синтаксического анализа:

## Options:
##   --verbose, -v  Verbose mode
##   --output=FILE  Output filename

source easyoptions || exit

if test -n "${verbose}"; then
    echo "output file is ${output}"
    echo "${arguments[@]}"
fi
5
ответ дан Renato Silva 17 August 2018 в 16:05
поделиться

Обратите внимание, что getopt(1) была короткой живой ошибкой от AT & amp; T.

getopt была создана в 1984 году, но уже похоронена в 1986 году, потому что она не была действительно полезной.

A доказательство того, что getopt очень устарело, состоит в том, что man-страница getopt(1) все еще упоминает "$*" вместо "$@", которая была добавлена ​​к Bourne Shell в 1986 году вместе с оболочкой getopts(1), встроенной для того, чтобы обратитесь к аргументам с пробелами внутри.

BTW: если вам интересно разобрать длинные параметры в сценариях оболочки, может быть интересно узнать, что реализация getopt(3) из libc (Solaris) и ksh93 оба добавили единую длинную опционную реализацию, которая поддерживает длинные опции как псевдонимы для коротких опций. Это приводит к тому, что ksh93 и Bourne Shell реализуют единый интерфейс для длинных опций с помощью getopts.

Пример для длинных параметров, взятых из страницы руководства Bourne Shell:

getopts "f:(file)(input-file)o:(output-file)" OPTX "$@"

показывает, как долго могут использоваться псевдонимы вариантов как в Bourne Shell, так и в ksh93.

См. справочную страницу недавней оболочки Bourne:

http://schillix.sourceforge.net/man/man1/bosh.1.html

и справочная страница для getopt (3) из OpenSolaris:

http://schillix.sourceforge.net/man/man3c/getopt.3c.html

и last, справочная страница getopt (1) для проверки устаревшего $ * :

http://schillix.sourceforge.net/man/man1/getopt.1.html

3
ответ дан schily 17 August 2018 в 16:05
поделиться

Я опаздываю на этот вопрос около 4 лет, но хочу вернуть. Я использовал более ранние ответы в качестве отправной точки, чтобы убрать мой старый синтаксический анализ adhoc. Затем я переработал следующий код шаблона. Он обрабатывает как длинные, так и короткие параметры, используя аргументы = или пробел, а также несколько коротких параметров, сгруппированных вместе. Наконец, он снова вставляет любые аргументы без параметров обратно в переменные $ 1, $ 2 .. Надеюсь, это полезно.

#!/usr/bin/env bash

# NOTICE: Uncomment if your script depends on bashisms.
#if [ -z "$BASH_VERSION" ]; then bash $0 $@ ; exit $? ; fi

echo "Before"
for i ; do echo - $i ; done


# Code template for parsing command line parameters using only portable shell
# code, while handling both long and short params, handling '-f file' and
# '-f=file' style param data and also capturing non-parameters to be inserted
# back into the shell positional parameters.

while [ -n "$1" ]; do
        # Copy so we can modify it (can't modify $1)
        OPT="$1"
        # Detect argument termination
        if [ x"$OPT" = x"--" ]; then
                shift
                for OPT ; do
                        REMAINS="$REMAINS \"$OPT\""
                done
                break
        fi
        # Parse current opt
        while [ x"$OPT" != x"-" ] ; do
                case "$OPT" in
                        # Handle --flag=value opts like this
                        -c=* | --config=* )
                                CONFIGFILE="${OPT#*=}"
                                shift
                                ;;
                        # and --flag value opts like this
                        -c* | --config )
                                CONFIGFILE="$2"
                                shift
                                ;;
                        -f* | --force )
                                FORCE=true
                                ;;
                        -r* | --retry )
                                RETRY=true
                                ;;
                        # Anything unknown is recorded for later
                        * )
                                REMAINS="$REMAINS \"$OPT\""
                                break
                                ;;
                esac
                # Check for multiple short options
                # NOTICE: be sure to update this pattern to match valid options
                NEXTOPT="${OPT#-[cfr]}" # try removing single short opt
                if [ x"$OPT" != x"$NEXTOPT" ] ; then
                        OPT="-$NEXTOPT"  # multiple short opts, keep going
                else
                        break  # long form, exit inner loop
                fi
        done
        # Done with that param. move to next
        shift
done
# Set the non-parameters back into the positional parameters ($1 $2 ..)
eval set -- $REMAINS


echo -e "After: \n configfile='$CONFIGFILE' \n force='$FORCE' \n retry='$RETRY' \n remains='$REMAINS'"
for i ; do echo - $i ; done
36
ответ дан Shane Day 17 August 2018 в 16:05
поделиться
  • 1
    Этот код не может обрабатывать параметры с такими аргументами: -c1. И использование = для разделения коротких опций из их аргументов необычно ... – Robert Siemer 6 December 2015 в 14:47
  • 2
    Я столкнулся с двумя проблемами с этим полезным фрагментом кода: 1) «shift» в случае "-c = foo" заканчивается употреблением следующего параметра; и 2) 'c' не следует включать в "[cfr]" шаблон для комбинируемых коротких опций. – sfnd 6 June 2016 в 19:28

Расширение превосходного ответа от @guneysus - вот настройка, которая позволяет пользователю использовать любой синтаксис, который они предпочитают, например

command -x=myfilename.ext --another_switch 

vs

command -x myfilename.ext --another_switch

То есть говорят, что равные могут быть заменены пробелами.

Эта «нечеткая интерпретация» может быть не по вашему вкусу, но если вы создаете сценарии, которые взаимозаменяемы с другими утилитами (как в случае с моими, которые должны работать с ffmpeg), гибкость полезна .

STD_IN=0

prefix=""
key=""
value=""
for keyValue in "$@"
do
  case "${prefix}${keyValue}" in
    -i=*|--input_filename=*)  key="-i";     value="${keyValue#*=}";; 
    -ss=*|--seek_from=*)      key="-ss";    value="${keyValue#*=}";;
    -t=*|--play_seconds=*)    key="-t";     value="${keyValue#*=}";;
    -|--stdin)                key="-";      value=1;;
    *)                                      value=$keyValue;;
  esac
  case $key in
    -i) MOVIE=$(resolveMovie "${value}");  prefix=""; key="";;
    -ss) SEEK_FROM="${value}";          prefix=""; key="";;
    -t)  PLAY_SECONDS="${value}";           prefix=""; key="";;
    -)   STD_IN=${value};                   prefix=""; key="";; 
    *)   prefix="${keyValue}=";;
  esac
done
11
ответ дан unsynchronized 17 August 2018 в 16:05
поделиться

Использовать «аргументы» модуля из bash-modules

Пример:

#!/bin/bash
. import.sh log arguments

NAME="world"

parse_arguments "-n|--name)NAME;S" -- "$@" || {
  error "Cannot parse command line."
  exit 1
}

info "Hello, $NAME!"
1
ответ дан Volodymyr M. Lisivka 17 August 2018 в 16:05
поделиться
22
ответ дан Community 6 September 2018 в 10:30
поделиться
0
ответ дан Thanh Trung 29 October 2018 в 17:05
поделиться
Другие вопросы по тегам:

Похожие вопросы: