我已经知道了getopts,这很好,但是即使是强制性的参数也必须有一个标志,这很烦人。
理想情况下,我希望能够拥有一个以这种形式接收参数的脚本:
script.sh [optional arguments] [anything required]
例如
script.sh -rvx output_file.txt
脚本说你必须有一个输出文件。有没有简单的方法呢?
据我所知,对于getopts,它必须看起来像:script.sh -rvx -f output_file.txt
,这不是很干净。
如果需要,我也可以使用python,但只有2.4可用,这有点过时了。
答案 0 :(得分:29)
不要使用内置的getopts,而是使用getopt(1)。它们(巧妙地)是不同的,并且做得很好。对于您的情况,您可以这样做:
#!/bin/bash
eval set -- $(getopt -n $0 -o "-rvxl:" -- "$@")
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[@]}"
编辑:为了完整性,我提供了一个处理需要参数的选项的例子。
答案 1 :(得分:11)
如果您使用的是getops,请在案例陈述后按$OPTIND-1
换班。那么$*
中剩下的就是其他一切,这可能是你想要的。
shift $(( ${OPTIND} - 1 )); echo "${*}"
答案 2 :(得分:4)
你正在遭受幻想;使用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
这只是一个半正式的近似值;如果您在可选参数后面没有提供任何文件名,则脚本会静静地执行任何操作。但是,如果需要,可以检查“shift”后是否存在强制参数。我的另一个剧本确实有强制性的论据。它包含:
...
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
循环来获取子选项(并且可能在'文件名'参数上运行最后一个循环)。
生活不是很有趣吗?
答案 3 :(得分:2)
我听到一个完全相反的事情,你不应该使用getopt
,而应该getopts
内置。
Cross-platform getopt for a shell script
永远不要使用getopt(1)。 getopt无法处理空参数字符串,或 嵌入空格的参数。请忘记它 存在。
POSIX shell(以及其他)提供可安全使用的getopts 代替。
答案 4 :(得分:1)
这是另一种“Option-ize你的shell脚本”的方法(不使用getopt或getopts):
http://bsdpants.blogspot.com/2007/02/option-ize-your-shell-scripts.html