getopt 没有按某种顺序获取参数

getopt not getting parameters in some order

我正在使用 getopt 从命令行解析参数,但我无法使其识别可选参数顺序。

我想实现这些案例:

$ ./program -i file.html -o output.html #ex 1
$ ./program -i -o file.html output.html #ex 2
$ ./program -o output.html -i file.html #ex 3

我的代码是这样的

while((c = getopt(argc, argv, "hi:o:")) != -1) {
    switch(c) {
        case 'h':
            //prints the help file
            break;
        case 'i':
            ivalue = optarg;
            break;
        case 'f':
            fvalue = optarg;
            break;
        case '?':
            //prints an error
            break;
        default:
            abort();
    }
}

为了更好地调试这个我也在 while

之外写了
for(int i = optind; i < argc; i++) {
    printf("non optional argument %s\n", argv[i]);
    return 0;
}

因此示例 1 和 3 正常工作,而示例 2 没有直接获取参数。起初我认为这个函数根本不可能,但后来在 this 示例中我看到它是。

还有一个额外的问题:为什么不带参数调用程序不会abort()

我正在使用 ubuntu 15.10 和 gcc 5.2.1 安装了 apt-get(不知道是否有用,但更安全,抱歉)。

getopt 无法实现您想要执行的操作。 optstring 中的 "i:" 意味着如果 -i 选项存在,它必须有参数。在您的第二个示例中,这意味着 -o 被解释为 -i 的参数而不是选项。

如果您在 Linux 上并与 glibc 链接,您可以使用 :: GNU 扩展(例如 "hi::o:")使 -i 接受一个可选参数.但是,这会破坏您的第一个和第三个示例,因为可选参数需要立即出现在选项之后(例如 -ifile.html)。

in this example I saw it was [possible].

不,你没有。您的选项 -i 需要一个参数,但在您的非工作情况下,您尝试在 -i 及其参数之间插入一个不同的选项。这是不允许的——如果一个选项带有一个参数,无论是可选的还是必需的,该参数(如果提供)必须紧跟在选项字母之后。您链接的示例没有显示不同。

您尝试做的事情不仅不受 getopt() 支持,而且不遵循标准的 Unix 约定。此外,尽管对于带有 required 参数的选项,它可以按照您想象的方式工作,但对于带有 optional 参数的选项,它是完全行不通的。当参数是可选的时,如果不需要直接跟随它们,则无法将它们与它们的选项正确匹配。

There's also a bonus question: how come calling the program with no parameters doesn't abort()?

为什么会这样?如果在程序的命令行上没有指定任何选项,那么在第一次调用时 getopt() returns -1,并且永远不会执行循环体。要从您的代码中获取 abort() ,您需要指定选项字符串中的选项,但您没有提供特定案例,或者需要参数但没有提供的选项参数(在事件 getopt() returns ':' 中,您没有提供具体案例)。由于目前编写的代码,需要 -o,有或没有参数,或者 -i 没有参数。

为了解决这个问题 "limitation"...我更愿意称之为预期功能,我使用了标志和函数。您遍历每个 getopt 参数并在传递时设置标志和变量(在某些情况下发送到函数以获取 returns 并取消设置其他标志,如果您希望一个特定的标志优先于另一个标志。然后一旦完成您进行一些心算以确定以何种顺序发生的事情。

例如,对于使最终用户更容易设置 ACL 的脚本(伪代码区域已明确标记):

getHelp () {
    printf "\nThis does stuff with [OPTIONS] and stuff...\n\n"
    exit 0
}
main () {
    if [[ $targetFile == "-h" ]]; then
        usage
        exit 0
    fi
    [[ ! -f $targetFile ]] && [[ ! -d $targetF ]] && {
        printf "\nYou must specify a target File or Folder. Exiting.\n"
        exit 1
    }
    modeOctal=1
    if [[ readMode -eq 1 ]] || [[ writeMode -eq 1 ]]; then
        printf "We're doing stuff to this file or folder...\n"
        if [[ readMode -eq 1 ]]; then
            modeOctal=`expr $modeOctal + 4`
        elif [[ writeMode -eq 1 ]]; then
            modeOctal=`expr $modeOctal + 2`
        fi
        *code to do stuff to setfacl on target*
    fi
    if [[ removeMode -eq 1 ]]; then
        *code to do stuff to setfacl -x permissions on target*
    fi
while true; do
    case "" in
        -h)
            getHelp
            exit 0
        ;;
        -r)
            readMode=1
            ;;
        -w)
            writeMode=1
            ;;
        -R)
            readMode=0
            writeMode=0
            removeMode=1
            ;;
        -t)
            shift
            targetFile=
            targetIsSet=1
            ;;
        --)
            shift
            break
            ;;
    esac
    shift
done

if [[ $targetIsSet == 1 ]]; then
    main
    exit 0
fi

exit 0

因为你可以指望事情每次都以相同的顺序进行,你可以确保无论你以什么顺序进行,你的执行都是一致的。在这个例子中,你也允许有人使用 -t 选项这需要第二个参数来使用“-h”来获得帮助。

如果下一项是其他选项(例如:-i -o output.file-o -i input.file)