使用 getopt 根据选项标志的顺序更改程序的行为
Behavior of program changing based on the order of option flags using getopt
我正在用 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] = '[=11=]';
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] = '[=11=]';
sprintf(buffer, "%*d\t", 6, lineNumber++);
strcat(buffer, tmp);
}
} else
if (nflag) {
char *tmp = strdup(buffer);
buffer[0] = '[=11=]';
sprintf(buffer, "%*d\t", 6, lineNumber++);
strcat(buffer, tmp);
}
if (eflag) {
length = strlen(buffer);
buffer[length] = '$';
buffer[length + 1] = '[=11=]';
}
fprintf(stdout, "%s\n", buffer);
}
fclose(fp);
currentFile++;
}
return 0;
}
这是因为getopt中的optstring:
int getopt(int argc, char *const argv[], const char *optstring);
根据手册页,optstring 的行为是:
optstring is a string containing the legitimate option
characters. If such a character is followed by a colon, the
option requires an argument, so getopt() places a pointer to the
following text in the same argv-element, or the text of the
following argv-element, in optarg. Two colons mean an option
takes an optional arg; if there is text in the current argv-
element (i.e., in the same word as the option name itself, for
example, "-oarg"), then it is returned in optarg, otherwise
optarg is set to zero.
下面的语句意味着只有 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
这可能仍然存在错误。请务必尝试一下,如果有问题请告诉我。
我正在用 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] = '[=11=]';
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] = '[=11=]';
sprintf(buffer, "%*d\t", 6, lineNumber++);
strcat(buffer, tmp);
}
} else
if (nflag) {
char *tmp = strdup(buffer);
buffer[0] = '[=11=]';
sprintf(buffer, "%*d\t", 6, lineNumber++);
strcat(buffer, tmp);
}
if (eflag) {
length = strlen(buffer);
buffer[length] = '$';
buffer[length + 1] = '[=11=]';
}
fprintf(stdout, "%s\n", buffer);
}
fclose(fp);
currentFile++;
}
return 0;
}
这是因为getopt中的optstring:
int getopt(int argc, char *const argv[], const char *optstring);
根据手册页,optstring 的行为是:
optstring is a string containing the legitimate option characters. If such a character is followed by a colon, the option requires an argument, so getopt() places a pointer to the following text in the same argv-element, or the text of the following argv-element, in optarg. Two colons mean an option takes an optional arg; if there is text in the current argv- element (i.e., in the same word as the option name itself, for example, "-oarg"), then it is returned in optarg, otherwise optarg is set to zero.
下面的语句意味着只有 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
这可能仍然存在错误。请务必尝试一下,如果有问题请告诉我。