Как создать bash script, который принимает аргументы?
Я уже знаю о getopts, и это прекрасно, но раздражает, что для обязательных аргументов вам нужно иметь флаг.
В идеале я хотел бы иметь script, который получает аргументы в этой форме:
script.sh [optional arguments] [anything required]
например
script.sh -rvx output_file.txt
где script говорит, что у вас есть выходной файл. Есть ли простой способ сделать это?
Насколько я знаю, с getopts это должно выглядеть так: script.sh -rvx -f output_file.txt
, и это просто не очень чисто.
Я также могу использовать python, если это необходимо, но имеет только доступную версию 2.4, которая немного устарела.
Ответы
Ответ 1
Не используйте встроенный getopts, вместо этого используйте getopt (1). Они (тонко) разные и хорошо выполняют разные вещи. Для сценария вы можете сделать это:
#!/bin/bash
eval set -- $(getopt -n $0 -o "-rvxl:" -- "[email protected]")
declare r v x l
declare -a files
while [ $# -gt 0 ] ; do
case "$1" in
-r) r=1 ; shift ;;
-v) v=1 ; shift ;;
-x) x=1 ; shift ;;
-l) shift ; l="$1" ; shift ;;
--) shift ;;
-*) echo "bad option '$1'" ; exit 1 ;;
*) files=("${files[@]}" "$1") ; shift ;;
esac
done
if [ ${#files} -eq 0 ] ; then
echo output file required
exit 1
fi
[ ! -z "$r" ] && echo "r on"
[ ! -z "$v" ] && echo "v on"
[ ! -z "$x" ] && echo "x on"
[ ! -z "$l" ] && echo "l == $l"
echo "output file(s): ${files[@]}"
EDIT: для полноты я привел пример обработки опции, требующей аргумента.
Ответ 2
Если вы используете getops, просто смените $OPTIND-1
после вашего оператора case. Тогда то, что осталось в $*
, будет всем остальным, что, вероятно, вам нужно.
shift $(( ${OPTIND} - 1 )); echo "${*}"
Ответ 3
Вы страдаете от иллюзий; использование getopts
не требует обязательных аргументов с префиксом буквы флага. Я попытался найти подходящий пример из моего корпуса скриптов; это полуприличное приближение. Он называется rcsunco
и используется для отмены выписки из RCS. Я не изменял это через некоторое время, я вижу; Я использую его довольно часто (потому что я еще не полностью перешел из RCS).
#!/bin/sh
#
# "@(#)$Id: rcsunco.sh,v 2.1 2002/08/03 07:41:00 jleffler Exp $"
#
# Cancel RCS checkout
# -V print version number
# -n do not remove or rename checked out file (like SCCS unget) (default)
# -r remove checked out file (default)
# -k keep checked out file as $file.keep
# -g checkout (unlocked) file after clean-up
# -q quiet checkout
: ${RCS:=rcs}
: ${CO:=co}
remove=yes
keep=no
get=no
quiet=
while getopts gknqrV opt
do
case $opt in
V) echo "`basename $0 .sh`: RCSUNCO Version $Revision: 2.1 $ ($Date: 2002/08/03 07:41:00 $)" |
rcsmunger
exit 0;;
g) get=yes;;
k) keep=yes;;
n) remove=no;;
q) quiet=-q;;
r) remove=yes;;
*) echo "Usage: `basename $0 .sh` [-{n|g}][-{r|k}] file [...]" 1>&2
exit 1;;
esac
done
shift $(($OPTIND-1))
for file in $*
do
rfile=$(rfile $file)
xfile=$(xfile $rfile)
if $RCS -u $rfile
then
if [ $keep = yes ]
then
if [ -f $xfile ]
then
mv $xfile $xfile.keep
echo "$xfile saved in $xfile.keep"
fi
elif [ $remove = yes ]
then rm -f $xfile
fi
if [ $get = yes ] && [ $remove = yes -o $keep = yes ]
then $CO $quiet $rfile
fi
fi
done
Это только полупринятое приближение; script тихо ничего не делает, если вы не укажете никаких имен файлов после дополнительных аргументов. Однако, если вам нужно, вы можете проверить, что обязательные аргументы присутствуют после "shift". У другого script у меня есть обязательные аргументы. Он содержит:
...
shift $(($OPTIND - 1))
case $# in
2) case $1 in
install) MODE=Installation;;
uninstall) MODE=Uninstallation;;
*) usage;;
esac;;
*) usage;;
esac
Итак, эта команда (jlss
) может принимать необязательные аргументы, такие как -d $HOME
, но требует либо install
, либо uninstall
, за которым следует имя что-то для установки. Основной способ использования:
jlss install program
Но дополнительный режим:
jlss -d $HOME -u me -g mine -x -p install program
Я не показывал все jlss
, потому что у него около 12 вариантов - он не такой компактный, как rcsunco
.
Если вы имели дело с обязательными аргументами перед аргументами опции, вам нужно было бы сделать немного больше работы:
- Вы должны подобрать обязательные аргументы, сместив их с пути.
- Затем вы обрабатываете необязательные аргументы с помощью флагов.
- Наконец, в случае необходимости вы обрабатываете дополнительные аргументы "имя файла".
Если вы имеете дело с обязательными аргументами, чередующимися с аргументами опций (как до, так и после обязательных), у вас еще больше работы. Это используется многими системами VCS; CVS и GIT имеют возможность:
git -global option command [-sub option] [...]
Здесь вы запускаете один цикл getopts
для получения глобальных опций; забрать обязательные аргументы; и запустите второй цикл getopts
, чтобы получить подпараметры (и, возможно, запустить конечный цикл над аргументами "имя файла" ).
Не радость жизни?
Ответ 4
И я услышал совершенно противоположную вещь, что вы не должны использовать getopt
, но встроенный getopts
.
Кросс-платформенный getopt для оболочки script
Никогда не используйте getopt (1). getopt не может обрабатывать строки с пустыми аргументами или аргументы со встроенным пробелом. Пожалуйста, забудьте, что это когда-либо существовала.
Оболочка POSIX (и другие) предлагает getopts, который безопасен в использовании вместо этого.
Ответ 5
Здесь еще один способ "Option -ize your shell scripts" (без использования getopt или getopts):
http://bsdpants.blogspot.com/2007/02/option-ize-your-shell-scripts.html