我想编写一个需要-c
和-f
的脚本,其中每个脚本都需要一个选项。
当我在下面运行我的脚本时,我会遇到一些意外错误:
$ ./user.sh -c
./user.sh: option requires an argument -- c
Usage: user.sh -c username -f filename
-c username
-f SSH public key
$ ./user.sh -c gg
Error: You have not given a filename.
在第一种情况下,我希望它说我错过了-c
的选项,而在第二种情况下我会喜欢它说我错过了-f
。
问题
如何处理此类错误,以及我做错了什么?
user.sh
#!/bin/bash
usage () {
echo "Usage: user.sh -c username -f filename"
echo " -c username"
echo " -f SSH public key"
echo ""
}
if ! [ "$*" ]; then
usage
exit 1
fi
while getopts "c:f:" opt; do
case $opt in
c) user=$OPTARG;;
f) filename=$OPTARG;;
\?)
echo
usage
exit 1;;
*) echo "Internal error: Unknown option.";;
esac
done
if ! [ $filename ]; then
echo "Error: You have not given a filename."
exit 1
fi
if ! [ $user ]; then
echo "Error: You have not given an username."
exit 1
fi
答案 0 :(得分:3)
c:
表示“-c
选项必须后跟用户名”。
错误消息显示“-c
选项后面没有用户名”。
当然,它没有提到'用户名',但那是因为它不知道选项-c
后面是什么。
getopts
内置无法处理强制选项;你必须通过检查强制选项实际上是否通过来为自己编码。如果指定两次相同的选项,它也不担心;如果重要的话,你的代码必须处理它。 (让最后一个指定值生效很容易。)
现代风格是在强制性参数之前避免使用选项字母。我并不完全赞成改变;这意味着参数的排序变得至关重要,因为使用选项字母来指示后面的内容不是。如果没有选项字母,您可以写:./user.sh username filename
,但是使用选项字母,您可以编写其中任何一个并期望它能够正常工作:
./user.sh -c username -f filename
./user.sh -f filename -c username
请注意,您有责任担心额外的参数。您通常会使用:
shift $(($OPTIND - 1))
删除已处理的参数,然后您可以执行以下操作:
case "$#" in
(0) : No extra arguments - OK;;
(*) echo "$0: Too many arguments" >&2; exit 1;;
esac
该主题的变化。请注意,错误报告将发送到标准错误,而不是标准输出 - >&2
重定向会将标准输出(文件描述符1)发送到标准错误(文件描述符2)。
为避免歧义,我会对您的使用功能进行一些不同的编码:
usage()
{
{
echo "Usage: user.sh -c username -f filename"
echo " -c username Name of user to connect as"
echo " -f filename SSH public key file"
echo ""
} >&2
}
内部大括号进行I / O重定向,无需启动子shell。当您需要将多个echo命令发送到同一个地方时,这非常有用。我还提供了一些不同的详细信息,这样用户就不会想到“SSH公钥”是遵循-f
的三个参数。如果有任何纯选项标志,则会跟随空白:
echo " -V Print version information and exit"