Передача именованных аргументов в сценарии оболочки

114

Есть ли простой способ передать (получить) именованные параметры в сценарий оболочки?

Например,

my_script -p_out '/some/path' -arg_1 '5'

А внутри my_script.shпринимают их как:

# I believe this notation does not work, but is there anything close to it?
p_out=$ARGUMENTS['p_out']
arg1=$ARGUMENTS['arg_1']

printf "The Argument p_out is %s" "$p_out"
printf "The Argument arg_1 is %s" "$arg1"

Это возможно в Bash или Zsh?

Амелио Васкес-Рейна
источник
2
взгляните на docopt - он помогает с именованными параметрами и проверяет входные данные
Beat
Релевантно: stackoverflow.com/questions/5499472/…
Каз

Ответы:

34

Вероятно, ближайший к этому синтаксис:

p_out='/some/path' arg_1='5' my_script
Хауке Лагинг
источник
7
В связи с этим, если -kопция установлена ​​в вызывающей оболочке, то my_script p_out='/some/path' arg_1='5'имеет тот же эффект. (Все аргументы в форме назначения добавляются в среду, а не только в те назначения, которые предшествуют команде.)
chepner
13
Раньше я любил этот синтаксис, но он имеет БОЛЬШУЮ оговорку: после выполнения команды / функции эти переменные все равно будут определены в текущей области видимости! Например: x=42 echo $x; echo $xчто означает, что при следующем выполнении my_script, если p_outоно опущено, оно будет придерживаться значения, переданного в последний раз !! ( '/some/path')
Лукас Кимон
@LucasCimon Можете ли вы не unsetих после первого выполнения, сбросьте их до следующего выполнения?
Никос Александрис
2
@ LucasCimon Это не правильно. x=42 echo $xдаже не выводит ничего, если $xне было определено ранее.
Хауке Лагинг
Вы правы @HaukeLaging, спасибо за исправление
Лукас Кимон
148

Если вы не возражаете ограничиться однобуквенными именами аргументов, т. my_script -p '/some/path' -a5Е. В bash вы можете использовать встроенные getopts, например,

#!/bin/bash

while getopts ":a:p:" opt; do
  case $opt in
    a) arg_1="$OPTARG"
    ;;
    p) p_out="$OPTARG"
    ;;
    \?) echo "Invalid option -$OPTARG" >&2
    ;;
  esac
done

printf "Argument p_out is %s\n" "$p_out"
printf "Argument arg_1 is %s\n" "$arg_1"

Тогда вы можете сделать

$ ./my_script -p '/some/path' -a5
Argument p_out is /some/path
Argument arg_1 is 5

Существует полезное руководство для небольших getopts, или вы можете ввести его help getoptsв командной строке.

steeldriver
источник
25
Это должен быть принятый ответ
Каушик Ghose
3
Я знаю, что это немного устарело, но почему только 1 буква для аргументов?
Кевин
1
Я реализовал это (но с iи d). Когда я запускаю его, my_script -i asd -d asdя получаю пустую строку для dаргумента. Когда я запускаю его, my_script -d asd -i asdя получаю пустую строку для обоих аргументов.
Milkncookiez
3
@Milkncookiez - у меня была похожая проблема - я не включал «:» после последнего аргумента («w» в моем случае). Как только я добавил «:», он начал работать как положено
Дерек
37

Я украл это на drupal.org , но вы могли бы сделать что-то вроде этого:

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

Единственное предостережение в том, что вы должны использовать синтаксис my_script --p_out=/some/path --arg_1=5.

CDMO
источник
7
Предостережение не обязательно. :) Вы можете иметь следующие условия:-c|--condition)
Milkncookiez
28

Я использую этот скрипт и работает как шарм:

for ARGUMENT in "$@"
do

    KEY=$(echo $ARGUMENT | cut -f1 -d=)
    VALUE=$(echo $ARGUMENT | cut -f2 -d=)   

    case "$KEY" in
            STEPS)              STEPS=${VALUE} ;;
            REPOSITORY_NAME)    REPOSITORY_NAME=${VALUE} ;;     
            *)   
    esac    


done

echo "STEPS = $STEPS"
echo "REPOSITORY_NAME = $REPOSITORY_NAME"

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

bash my_scripts.sh  STEPS="ABC" REPOSITORY_NAME="stackexchange"

Консольный результат:

STEPS = ABC
REPOSITORY_NAME = stackexchange

STEPS и REPOSITORY_NAME готовы к использованию в сценарии.

Неважно, в каком порядке находятся аргументы.

JRichardsz
источник
4
Это аккуратно и должно быть принятым ответом.
Мигельморин
15

С помощью zshвы бы использовали zparseopts:

#! /bin/zsh -
zmodload zsh/zutil
zparseopts -A ARGUMENTS -p_out: -arg_1:

p_out=$ARGUMENTS[--p_out]
arg1=$ARGUMENTS[--arg_1]

printf 'Argument p_out is "%s"\n' "$p_out"
printf 'Argument arg_1 is "%s"\n' "$arg_1"

Но вы бы назвали сценарий с myscript --p_out foo.

Обратите внимание, что zparseoptsне поддерживается сокращение длинных опций или --p_out=fooсинтаксиса, как в GNU getopt(3).

Стефан Шазелас
источник
Знаете ли вы, почему zparseopts использует только одну черту для аргументов, тогда как в []2 это тире? Не имеет смысла!
Тимо
@ Тимо, см. info zsh zparseoptsПодробности
Стефан
9

Я только что придумал этот скрипт

while [ $# -gt 0 ]; do

   if [[ $1 == *"--"* ]]; then
        v="${1/--/}"
        declare $v="$2"
   fi

  shift
done

передать его как my_script --p_out /some/path --arg_1 5и затем в сценарии, который вы можете использовать $arg_1и $p_out.

Шахзад Малик
источник
Мне нравится это решение в ksh88 мне пришлось v=``echo ${1} | awk '{print substr($1,3)}'`` typeset $v="$2"(Удалить одну обратные одиночные кавычки с каждой стороны)
хол
-2

Если функция или приложение имеет больше нуля аргументов, у них всегда есть последний аргумент.

Если вы хотите прочитать опцию флага и пары значений, как в: $ ./t.sh -o output -i input -l last

И вы хотите принять переменное количество пар опция / значение,

И не хочу огромное "если .. тогда .. еще .. фи" дерево,

Затем, после проверки количества аргументов, отличных от нуля и четного,

Напишите цикл while с этими четырьмя операторами eval в качестве тела, за которым следует оператор case, использующий два значения, определенные в каждом проходе цикла.

Сложная часть сценария демонстрируется здесь:

#!/bin/sh    

# For each pair - this chunk is hard coded for the last pair.
eval TMP="'$'$#"
eval "PICK=$TMP"
eval TMP="'$'$(($#-1))"
eval "OPT=$TMP"

# process as required - usually a case statement on $OPT
echo "$OPT \n $PICK"

# Then decrement the indices (as in third eval statement) 

:<< EoF_test
$ ./t.sh -o output -i input -l last
-l 
last
$ ./t.sh -o output -l last
-l 
last
$ ./t.sh  -l last
-l 
last
EoF_test
knc1
источник
-3
mitsos@redhat24$ my_script "a=1;b=mitsos;c=karamitsos"
#!/bin/sh
eval "$1"

вы только что ввели параметры командной строки в область действия скрипта !!

thettalos
источник
3
Это не работает с синтаксисом указанного OP; они хотят-a 1 -b mitsos -c karamitsos
Михаил Мрозек