Другое решение без getopt [s], POSIX, старого стиля Unix
Аналогично решению, опубликованному Бруно Броноским, это здесь без использования 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...
Не в этом решении
Необязательные аргументы
Опции с необязательными аргументами должны быть возможны с небольшим количеством работы, например, смотря вперед, есть ли блок без дефиса; тогда пользователю потребуется поставить дефис перед каждым блоком, следующим за блоком, с параметром, имеющим необязательный параметр. Может быть, это слишком сложно, чтобы общаться с пользователем, поэтому в этом случае лучше просто использовать ведущий дефис.
Все становится еще сложнее с несколькими возможными параметрами. Я бы не советовал делать опции, пытаясь быть умными, определяя, может ли аргумент быть для него или нет (например, если опция просто принимает число в качестве необязательного аргумента), потому что это может сломаться в будущем.
Я лично предпочитаю дополнительные опции вместо необязательных аргументов.
Опция аргументов представлена знаком равенства
Как и с необязательными аргументами, я не фанат этого (кстати, есть ли тема для обсуждения плюсов и минусов различных стилей параметров?), Но если вы хотите это, вы, вероятно, можете реализовать это сами, как это было сделано на http: // mywiki.wooledge.org/BashFAQ/035#Manual_loop с оператором --long-with-arg=?*
case и последующим удалением знака равенства (кстати, это сайт, который говорит, что создание объединения параметров возможно с некоторыми усилиями, но «оставил [это] как упражнение для читателя»). «что заставило меня поверить им на слово, но я начал с нуля).
Другие заметки
POSIX-совместимый, работает даже на старых установках Busybox, с которыми мне приходилось иметь дело (например cut
, head
с getopts
отсутствующим).
zparseopts -D -E -M -- d=debug -debug=d
И иметь и то-d
и другое--debug
в$debug
массиве,echo $+debug[1]
будет возвращать 0 или 1, если используется один из них. Ссылка: zsh.org/mla/users/2011/msg00350.html