当你已经知道要操作哪些文件以及要采取哪些操作时,编写 C 程序来处理文件很容易。 如果你将文件名“硬编码”到程序中,或者如果你的程序被编码为仅以一种方式执行操作,那么你的程序将始终知道该做什么。
但是,如果你的程序可以在每次运行时响应用户,则可以使你的程序 更加灵活。 让你的用户告诉你的程序要使用哪些文件或如何以不同的方式执行操作。 为此,你需要读取命令行。
读取命令行
当你在 C 中编写程序时,你可能会从声明开始
int main()
这是启动 C 程序的最简单方法。 但是,如果在括号中添加这些标准参数,你的程序可以读取在命令行中给出的选项
int main(int argc, char **argv)
argc
变量是参数计数或命令行上参数的数量。 这将始终是一个至少为一的数字。
argv
变量是一个双指针,一个字符串数组,其中包含来自命令行的参数。 数组中的第一个条目 *argv[0]
始终是程序的名称。 **argv
数组的其他元素包含其余的命令行参数。
我将编写一个简单的程序来回显在命令行中给它的选项。 这类似于 Linux echo
命令,但它也打印程序的名称。 它还使用 puts
函数在自己的行上打印每个命令行选项
#include <stdio.h>
int
main(int argc, char **argv)
{
int i;
printf("argc=%d\n", argc); /* debugging */
for (i = 0; i < argc; i++) {
puts(argv[i]);
}
return 0;
}
编译此程序并使用一些命令行选项运行它,你将看到你的命令行打印回给你,每个项目都在自己的行上
$ ./echo this program can read the command line
argc=8
./echo
this
program
can
read
the
command
line
此命令行将程序的 argc
设置为 8
,并且 **argv
数组包含八个条目:程序的名称,加上用户输入的七个单词。 与 C 程序中一样,数组从零开始,因此元素的编号为 0、1、2、3、4、5、6、7。 这就是为什么你可以使用比较 i < argc
的 for
循环处理命令行的原因。
你可以使用它来编写你自己的 Linux cat
或 cp
命令版本。 cat
命令的基本功能是显示一个或多个文件的内容。 这是一个简单的 cat
版本,它从命令行读取文件名
#include <stdio.h>
void
copyfile(FILE *in, FILE *out)
{
int ch;
while ((ch = fgetc(in)) != EOF) {
fputc(ch, out);
}
}
int
main(int argc, char **argv)
{
int i;
FILE *fileptr;
for (i = 1; i < argc; i++) {
fileptr = fopen(argv[i], "r");
if (fileptr != NULL) {
copyfile(fileptr, stdout);
fclose(fileptr);
}
}
return 0;
}
这个简单的 cat
版本从命令行读取文件名列表,并将每个文件的内容一次一个字符地显示到标准输出。 例如,如果我有一个名为 hello.txt
的文件,其中包含几行文本,我可以使用我自己的 cat
命令显示其内容
$ ./cat hello.txt
Hi there!
This is a sample text file.
使用此示例程序作为起点,你可以通过仅读取两个文件名来编写你自己的其他 Linux 命令版本,例如 cp
程序:一个要读取的文件和另一个要写入复制的文件。
读取命令行选项
从命令行读取文件名和其他文本很棒,但是如果你希望你的程序根据用户给它的 选项 来更改其行为怎么办? 例如,Linux cat
命令支持多个命令行选项,包括
-b
在非空白行旁边放置行号-E
将行尾显示为$
-n
-s
抑制打印重复的空白行-T
将制表符显示为^I
-v
^x
和M-x
表示法显示非打印字符,但换行符和制表符除外
这些单字母选项称为短选项,它们总是以单个连字符开头。 你通常会看到这些短选项单独编写,例如 cat -E -n
,但你也可以将短选项组合成单个选项字符串,例如 cat -En
。
幸运的是,有一种简单的方法可以从命令行读取这些选项。 所有 Linux 和 Unix 系统都包含一个特殊的 C 库,名为 getopt
,它在 unistd.h
头文件中定义。 你可以在你的程序中使用 getopt
来读取这些短选项。
与其他 Unix 系统不同,Linux 上的 getopt
将始终确保你的短选项出现在命令行的前面。 例如,假设用户输入 cat -E file -n
。 -E
选项在前,但 -n
选项在文件名之后。 但是,如果你使用 Linux getopt
,你的程序将始终表现得好像用户输入了 cat -E -n file
。 这使得处理变得轻而易举,因为 getopt
可以解析短选项,从而在命令行上为你留下一系列文件名,你的程序可以使用 **argv
数组读取这些文件名。
你可以像这样使用 getopt
#include <unistd.h>
int getopt(int argc, char **argv, char *optstring);
选项字符串 optstring
包含有效选项字符的列表。 如果你的程序只允许 -E
和 -n
选项,则可以使用 “En
” 作为你的选项字符串。
你通常在循环中使用 getopt
来解析命令行的选项。 在每次 getopt
调用时,该函数返回它在命令行上找到的下一个短选项,或者对于任何无法识别的短选项,返回 '?'
值。 当 getopt
找不到更多短选项时,它返回 -1
并将全局变量 optind
设置为 **argv
中所有短选项之后的下一个元素。
让我们看一个简单的例子。 这个演示程序不是 cat
及其所有选项的完整替代品,但它可以解析其命令行。 每次找到有效的命令行选项时,它都会打印一条简短的消息来指示已找到它。 在你自己的程序中,你可能改为设置一个变量或采取一些其他操作来响应该命令行选项
#include <stdio.h>
#include <unistd.h>
int
main(int argc, char **argv)
{
int i;
int option;
/* parse short options */
while ((option = getopt(argc, argv, "bEnsTv")) != -1) {
switch (option) {
case 'b':
puts("Put line numbers next to non-blank lines");
break;
case 'E':
puts("Show the ends of lines as $");
break;
case 'n':
puts("Put line numbers next to all lines");
break;
case 's':
puts("Suppress printing repeated blank lines");
break;
case 'T':
puts("Show tabs as ^I");
break;
case 'v':
puts("Verbose");
break;
default: /* '?' */
puts("What's that??");
}
}
/* print the rest of the command line */
puts("------------------------------");
for (i = optind; i < argc; i++) {
puts(argv[i]);
}
return 0;
}
如果你将此程序编译为 args
,则可以尝试不同的命令行,以查看它们如何解析 短选项,并始终为你留下 命令行的其余部分。 在最简单的情况下,所有选项都在前面,你会得到这个
$ ./args -b -T file1 file2
Put line numbers next to non-blank lines
Show tabs as ^I
------------------------------
file1
file2
现在尝试相同的命令行,但将两个短选项组合成一个选项字符串
$ ./args -bT file1 file2
Put line numbers next to non-blank lines
Show tabs as ^I
------------------------------
file1
file2
如果需要,getopt
可以“重新排序”命令行以处理顺序错误的短选项
$ ./args -E file1 file2 -T
Show the ends of lines as $
Show tabs as ^I
------------------------------
file1
file2
如果你的用户给出了不正确的短选项,getopt
会打印一条消息
$ ./args -s -an file1 file2
Suppress printing repeated blank lines
./args: invalid option -- 'a'
What's that??
Put line numbers next to all lines
------------------------------
file1
file2
下载速查表
getopt
可以做的事情比我展示的要多得多。 例如,短选项可以采用自己的选项,例如 -s string
或 -f file
。 你还可以告诉 getopt
在找到无法识别的选项时不显示错误消息。 使用 man 3 getopt
阅读 getopt(3)
手册页,以了解有关 getopt
可以为你做什么的更多信息。
如果你正在寻找关于 getopt()
和 getopt_long()
的语法和结构的温和提醒,下载我的 getopt 速查表。 一页演示了短选项,另一面演示了长选项,其中包含最少的可用代码以及你需要知道的全局变量列表。
评论已关闭。