Возможно ли смешивание getopts с позиционными параметрами?
Я хочу создать оболочку script в качестве оболочки для нескольких скриптов. Я хотел бы указать параметры для myshell.sh
с помощью getopts
и передать остальные параметры в том же порядке, что и указанный script.
Если myshell.sh
выполняется как:
myshell.sh -h hostname -s test.sh -d waittime param1 param2 param3
myshell.sh param1 param2 -h hostname param3 -d waittime -s test.sh
myshell.sh param1 -h hostname -d waittime -s test.sh param2 param3
Все вышеперечисленное должно иметь возможность вызова
test.sh param1 param2 param3
Возможно ли использовать параметры параметров в myshell.sh
и оставить оставшиеся параметры ниже script?
Ответы
Ответ 1
Извините за комментарий к старой теме, но подумал, что я отправлю для тех, как я, которые искали, как это сделать...
Я хотел сделать что-то похожее на OP, и я нашел нужную мне информацию здесь и здесь
По существу, если вы хотите сделать что-то вроде:
script.sh [options] ARG1 ARG2
Затем получите следующие варианты:
while getopts "h:u:p:d:" flag; do
case "$flag" in
h) HOSTNAME=$OPTARG;;
u) USERNAME=$OPTARG;;
p) PASSWORD=$OPTARG;;
d) DATABASE=$OPTARG;;
esac
done
И тогда вы можете получить свои позиционные аргументы следующим образом:
ARG1=${@:$OPTIND:1}
ARG2=${@:$OPTIND+1:1}
Дополнительная информация и подробная информация доступны по ссылке выше.
Надеюсь, что это поможет!
Ответ 2
Смешать опционы и аргументы:
ARGS=""
echo "options :"
while [ $# -gt 0 ]
do
unset OPTIND
unset OPTARG
while getopts as:c: options
do
case $options in
a) echo "option a no optarg"
;;
s) serveur="$OPTARG"
echo "option s = $serveur"
;;
c) cible="$OPTARG"
echo "option c = $cible"
;;
esac
done
shift $((OPTIND-1))
ARGS="${ARGS} $1 "
shift
done
echo "ARGS : $ARGS"
exit 1
Результат:
bash test.sh -a arg1 arg2 -s serveur -c cible arg3
options :
option a no optarg
option s = serveur
option c = cible
ARGS : arg1 arg2 arg3
Ответ 3
getopts
не будет анализировать сочетание параметров param1
и -n
.
Гораздо лучше поместить param1-3 в опции, подобные другим.
Кроме того, вы можете использовать уже существующие библиотеки, такие как shflags. Это довольно умный и простой в использовании.
И последний способ - написать свою собственную функцию для анализа параметров без getopts, просто итерируя все параметры через конструкцию case
. Это самый сложный способ, но это единственный способ точно соответствовать вашим ожиданиям.
Ответ 4
Я придумал один способ: getopts
можно расширить, чтобы действительно смешивать параметры и позиционные параметры. Идея состоит в том, чтобы чередовать вызов getopts
и назначение любых позиционных параметров, найденных в n1
, n2
, n3
и т.д.:
parse_args() {
_parse_args 1 "[email protected]"
}
_parse_args() {
local n="$1"
shift
local options_func="$1"
shift
local OPTIND
"$options_func" "[email protected]"
shift $(( OPTIND - 1 ))
if [ $# -gt 0 ]; then
eval test -n \${n$n+x}
if [ $? -eq 0 ]; then
eval n$n="\$1"
fi
shift
_parse_args $(( n + 1 )) "$options_func" "[email protected]"
fi
}
Тогда в случае OP вы можете использовать его как:
main() {
local n1='' n2='' n3=''
local duration hostname script
parse_args parse_main_options "[email protected]"
echo "n1 = $n1"
echo "n2 = $n2"
echo "n3 = $n3"
echo "duration = $duration"
echo "hostname = $hostname"
echo "script = $script"
}
parse_main_options() {
while getopts d:h:s: opt; do
case "$opt" in
d) duration="$OPTARG" ;;
h) hostname="$OPTARG" ;;
s) script="$OPTARG" ;;
esac
done
}
main "[email protected]"
Запуск показывает вывод:
$ myshell.sh param1 param2 -h hostname param3 -d waittime -s test.sh
n1 = param1
n2 = param2
n3 = param3
duration = waittime
hostname = hostname
script = test.sh
Просто доказательство концепции, но, возможно, это полезно для кого-то.
Примечание: есть, если одна функция, использующая parse_args
, вызывает другую функцию, которая использует parse_args
и, объявляет внешнюю функцию, например. local n4=''
, но внутренний не имеет значения и 4 или более позиционных параметров передается внутренней функции
Ответ 5
Просто перепутал quickie, который легко обрабатывает смесь опций и позиционных параметров (оставляя только позиционные параметры в [email protected]):
#!/bin/bash
while [ ${#} -gt 0 ];do OPTERR=0;OPTIND=1;getopts "p:o:hvu" arg;case "$arg" in
p) echo "Path: [$OPTARG]" ;;
o) echo "Output: [$OPTARG]" ;;
h) echo "Help" ;;
v) echo "Version" ;;
\?) SET+=("$1") ;;
*) echo "Coding error: '-$arg' is not handled by case">&2 ;;
esac;shift;[ "" != "$OPTARG" ] && shift;done
[ ${#SET[@]} -gt 0 ] && set "" "${SET[@]}" && shift
echo -e "=========\nLeftover (positional) parameters (count=$#) are:"
for i in `seq $#`;do echo -e "\t$i> [${!i}]";done
Пример вывода:
[[email protected]:~]$ ./test.sh 'aa bb' -h -v -u -q 'cc dd' -p 'ee ff' 'gg hh' -o ooo
Help
Version
Coding error: '-u' is not handled by case
Path: [ee ff]
Output: [ooo]
=========
Leftover (positional) parameters (count=4) are:
1> [aa bb]
2> [-q]
3> [cc dd]
4> [gg hh]
[[email protected]:~]$
Ответ 6
Вместо использования getopts
вы можете напрямую реализовать свой собственный анализатор аргументов bash. Возьмите это как рабочий пример. Он может одновременно обрабатывать аргументы name и position.
#!/bin/bash
function parse_command_line() {
local named_options;
local parsed_positional_arguments;
yes_to_all_questions="";
parsed_positional_arguments=0;
named_options=(
"-y" "--yes"
"-n" "--no"
"-h" "--help"
"-s" "--skip"
"-v" "--version"
);
function validateduplicateoptions() {
local item;
local variabletoset;
local namedargument;
local argumentvalue;
variabletoset="${1}";
namedargument="${2}";
argumentvalue="${3}";
if [[ -z "${namedargument}" ]]; then
printf "Error: Missing command line option for named argument '%s', got '%s'...\\n" "${variabletoset}" "${argumentvalue}";
exit 1;
fi;
for item in "${named_options[@]}";
do
if [[ "${item}" == "${argumentvalue}" ]]; then
printf "Warning: Named argument '%s' got possible invalid option '%s'...\\n" "${namedargument}" "${argumentvalue}";
exit 1;
fi;
done;
if [[ -n "${!variabletoset}" ]]; then
printf "Warning: Overriding the named argument '%s=%s' with '%s'...\\n" "${namedargument}" "${!variabletoset}" "${argumentvalue}";
else
printf "Setting '%s' named argument '%s=%s'...\\n" "${thing_name}" "${namedargument}" "${argumentvalue}";
fi;
eval "${variabletoset}='${argumentvalue}'";
}
# https://stackoverflow.com/questions/2210349/test-whether-string-is-a-valid-integer
function validateintegeroption() {
local namedargument;
local argumentvalue;
namedargument="${1}";
argumentvalue="${2}";
if [[ -z "${2}" ]];
then
argumentvalue="${1}";
fi;
if [[ -n "$(printf "%s" "${argumentvalue}" | sed s/[0-9]//g)" ]];
then
if [[ -z "${2}" ]];
then
printf "Error: The %s positional argument requires a integer, but it got '%s'...\\n" "${parsed_positional_arguments}" "${argumentvalue}";
else
printf "Error: The named argument '%s' requires a integer, but it got '%s'...\\n" "${namedargument}" "${argumentvalue}";
fi;
exit 1;
fi;
}
function validateposisionaloption() {
local variabletoset;
local argumentvalue;
variabletoset="${1}";
argumentvalue="${2}";
if [[ -n "${!variabletoset}" ]]; then
printf "Warning: Overriding the %s positional argument '%s=%s' with '%s'...\\n" "${parsed_positional_arguments}" "${variabletoset}" "${!variabletoset}" "${argumentvalue}";
else
printf "Setting the %s positional argument '%s=%s'...\\n" "${parsed_positional_arguments}" "${variabletoset}" "${argumentvalue}";
fi;
eval "${variabletoset}='${argumentvalue}'";
}
while [[ "${#}" -gt 0 ]];
do
case ${1} in
-y|--yes)
yes_to_all_questions="${1}";
printf "Named argument '%s' for yes to all questions was triggered.\\n" "${1}";
;;
-n|--no)
yes_to_all_questions="${1}";
printf "Named argument '%s' for no to all questions was triggered.\\n" "${1}";
;;
-h|--help)
printf "Print help here\\n";
exit 0;
;;
-s|--skip)
validateintegeroption "${1}" "${2}";
validateduplicateoptions g_installation_model_skip_commands "${1}" "${2}";
shift;
;;
-v|--version)
validateduplicateoptions branch_or_tag "${1}" "${2}";
shift;
;;
*)
parsed_positional_arguments=$((parsed_positional_arguments+1));
case ${parsed_positional_arguments} in
1)
validateposisionaloption branch_or_tag "${1}";
;;
2)
validateintegeroption "${1}";
validateposisionaloption g_installation_model_skip_commands "${1}";
;;
*)
printf "ERROR: Extra positional command line argument '%s' found.\\n" "${1}";
exit 1;
;;
esac;
;;
esac;
shift;
done;
if [[ -z "${g_installation_model_skip_commands}" ]];
then
g_installation_model_skip_commands="0";
fi;
}
Вы бы назвали эту функцию как:
#!/bin/bash
source ./function_file.sh;
parse_command_line "${@}";
Пример использования:
./test.sh as 22 -s 3
Setting the 1 positional argument 'branch_or_tag=as'...
Setting the 2 positional argument 'skip_commands=22'...
Warning: Overriding the named argument '-s=22' with '3'...
Рекомендации:
- example_installation_model.sh.md
- Проверка на правильное количество аргументов
- https://unix.stackexchange.com/questions/129391/passing-named-arguments-to-shell-scripts
- Пример использования getopts в bash
Ответ 7
Существуют некоторые стандарты для обработки параметров unix, а в программировании оболочки getopts
- лучший способ их принудительного применения. Почти любой современный язык (perl, python) имеет вариант на getopts
.
Это просто быстрый пример:
command [ options ] [--] [ words ]
-
Каждый параметр должен начинаться с тире, -
и должен состоять из одного символа.
-
В проекте GNU были введены Long Options, начиная с двух тире --
,
за которым следует целое слово, --long_option
. Проект AST KSH имеет getopts, который также поддерживает длинные параметры и длинные параметры, начинающиеся с одной тире, -
, как в find(1)
.
-
Параметры могут или не могут ожидать аргументы.
-
Любое слово, не начинающееся с тире, -
, завершит обработку опций.
-
Строка --
должна быть пропущена и закончится обработкой параметров.
-
Остальные аргументы остаются в виде позиционных параметров.
В Open Group есть раздел Utility Argument Syntax
Эрик Раймонд Искусство программирования Unix имеет главу о традиционных вариантах unix для букв опций и их значения.
Ответ 8
Вы можете попробовать этот трюк: после цикла с optargs просто используйте этот фрагмент
#shift away all the options so that only positional agruments
#remain in [email protected]
for (( i=0; i<OPTIND-1; i++)); do
shift
done
POSITIONAL="[email protected]"
Однако этот подход имеет ошибку:
- все параметры после первого позиционного аргумента вводятся getopts и рассматриваются как позиционные аргументы - события, которые являются правильными (см. пример вывода: -m и -c относятся к позиционным аргументам)
Возможно, у него еще больше ошибок...
Посмотрите на весь пример:
while getopts :abc opt; do
case $opt in
a)
echo found: -a
;;
b)
echo found: -b
;;
c)
echo found: -c
;;
\?) echo found bad option: -$OPTARG
;;
esac
done
#OPTIND-1 now points to the first arguments not beginning with -
#shift away all the options so that only positional agruments
#remain in [email protected]
for (( i=0; i<OPTIND-1; i++)); do
shift
done
POSITIONAL="[email protected]"
echo "positional: $POSITIONAL"
Вывод:
[[email protected] ~]# ./abc.sh -abc -de -fgh -bca haha blabla -m -c
found: -a
found: -b
found: -c
found bad option: -d
found bad option: -e
found bad option: -f
found bad option: -g
found bad option: -h
found: -b
found: -c
found: -a
positional: haha blabla -m -c