【问题标题】:Behavior of program changing based on the order of option flags using getopt使用 getopt 根据选项标志的顺序更改程序的行为
【发布时间】:2021-08-16 06:43:46
【问题描述】:

我正在用 C 语言编写一个 cat 命令克隆,当我更改选项标志的顺序时出现了奇怪的行为。

-s 选项标志压缩双倍行距。

-n 选项标志编号从 1 开始的每一行。

我已经通过以下方式检查了运行我的程序的区别:

$ diff <(./myCat -s spaces.txt) <(cat -s spaces.txt)
no difference

$ diff <(./myCat -n spaces.txt) <(cat -n spaces.txt)
no difference

$ diff <(./myCat -ns spaces.txt) <(cat -ns spaces.txt)
no difference

但是,运行./myCat -sn spaces.txt 只会压缩文本,不会对行编号。 谁能解释这种行为?我认为在这种情况下选项标志的顺序无关紧要。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char **argv) {

    FILE *fp;
    const int bufferSize = 4096;
    char buffer[bufferSize];
    int currentFile = 0;

    for (int i = 1; i < argc; i++) {
        if (argv[i][0] != '-') {
            currentFile = i;
            break;
        }
    }

    int bflag = 0, eflag = 0, nflag = 0, sflag = 0;
    int opt;

    while ((opt = getopt(argc, argv, "bens:?")) != -1) {
        switch(opt) {
          case 'b':
            bflag++;
            break;
          case 'e':
            eflag++;
            break;
          case 'n':
            nflag++;
            break;
          case 's':
             sflag++;
             break;
          case ':':
            printf("option needs a value\n");
            exit(1);
          case '?':
            printf("usage: cat [-bens] [file ...]\n");
            exit(1);
        }
    }
    
    while (currentFile < argc) {
        if (currentFile) {
            fp = fopen(argv[currentFile], "rb");
            if (fp == NULL) {
                fprintf(stderr, "%s: %s: No such file or directory",
                        argv[0], argv[currentFile]);
                exit(1);
            }
        }

        int lineNumber = 1;
        int lastLineBlank = 0;

        while (fgets(buffer, bufferSize, (fp == NULL ? stdin : fp))) {

            int length = strlen(buffer);
            buffer[length - 1] = '\0';

            if (sflag) {
                length = strlen(buffer);
                int currentLineBlank = (length <= 1) ? 1 : 0;
                if (lastLineBlank && currentLineBlank) {
                    continue;
                }
                lastLineBlank = currentLineBlank;
            }

            
            if (bflag) {
                length = strlen(buffer);
                if (length >= 1) {
                    char *tmp = strdup(buffer);
                    buffer[0] = '\0';
                    sprintf(buffer, "%*d\t", 6, lineNumber++);
                    strcat(buffer, tmp);
                }
            } else
            if (nflag) {
                char *tmp = strdup(buffer);
                buffer[0] = '\0';
                sprintf(buffer, "%*d\t", 6, lineNumber++);
                strcat(buffer, tmp);
            }

            if (eflag) {
                length = strlen(buffer);
                buffer[length] = '$';
                buffer[length + 1] = '\0';
            }

            fprintf(stdout, "%s\n", buffer);
        }

        fclose(fp);
        currentFile++;
    }

    return 0;
}

【问题讨论】:

    标签: c unix command


    【解决方案1】:

    这是因为 getopt 中的 optstring:

    int getopt(int argc, char *const argv[], const char *optstring);
    

    从手册页来看,optstring 的行为是:

    optstring 是一个包含合法选项的字符串 人物。如果这样的字符后跟冒号,则 选项需要一个参数,所以 getopt() 放置一个指向 在同一 argv 元素中跟随文本,或 在 argv 元素之后,在 optarg 中。两个冒号表示一个选项 接受一个可选参数;如果当前 argv 中有文本- 元素(即,与选项名称本身在同一个词中,对于 例如,“-oarg”),则在 optarg 中返回,否则 optarg 设置为零。

    下面的语句意味着只有s需要一个参数:

    getopt(argc, argv, "bens:?")
    

    ./a.out -n./a.out 上的原始代码段错误)

    看起来唯一的方法是使用-ns 或在文件名后面加上-n。但是getopt 的 GNU libc 示例非常好 - https://www.gnu.org/software/libc/manual/html_node/Example-of-Getopt.html
    我重写了一些代码,以便它处理所有标志但仍能正确处理文件名。它并不完美,但在这里:

    // I removed the for loop before this. It is not required here.
    // Please remove the for loop if you use this code.
    // only the case ':' has been removed here 
    while ((opt = getopt(argc, argv, "bens?")) != -1) {
        switch(opt) {
            case 'b':
                bflag++;
                break;
            case 'e':
                eflag++;
                break;
            case 'n':
                nflag++;
                break;
            case 's':
                sflag++;
                break;
            case '?':
                printf("usage: cat [-bens] [file ...]\n");
                exit(1);
        }
    }
    
    // optind is set by getopt. 
    // It is equal to the position of the immediate argument after the options.
    // this works because getopt permutes argv so that all the non-options are at the end
    currentFile = optind;
    
    // when no file name is provided
    if(currentFile == argc) {
        printf("Need a filename!\n");
        return 1;
    }
    

    其余代码保持不变。它适用于以下示例:

    • ./a.out -sn file
    • ./a.out -ns file
    • ./a.out -s file1 file2
    • ./a.out -s file1 -n file2

    这可能仍然存在错误。请尝试一下,如果有问题,请告诉我。

    【讨论】:

      猜你喜欢
      • 2021-09-07
      • 1970-01-01
      • 2013-01-02
      • 1970-01-01
      • 2019-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-27
      相关资源
      最近更新 更多