[apue] getopt 可能重排参数
看第21章时,介绍到了解析命令行的神器 getopt,了解了 linux 下处理通用命令行的方法。
命令行可分为参数与选项,其中不带 - 或 -- 前缀的为参数,对一个命令而言数量是固定的,多个参数之间的顺序也是固定的(不然命令没法区分);而选项就是带 - 或 -- 前缀的,可有可没有的,由用户的输入决定,另外选项也可以有自己的跟随参数,它们之间是没有顺序的。比如说:
> wget -c http: //strawberryperl .com /download/5 .30.2.1 /strawberry-perl-5 .30.2.1-32bit.msi |
这个 wget 命令行有两个输入,一个是选项 -c,表示断点续传;一个是参数 url,就是后面这一串下载的地址。
当然这只是一个简单的例子,并没有多个参数,也没有多个选项,选项也没有带自己的参数。后面我们会自己做一个复杂的例子,来做验证。
回到 getopt,它的作用就是简化对这些输入的处理。
如何简化呢,就是通过定义一个可接受的选项“模板”,然后通过不停调用 getopt 来将所有选项解析出来,
最后剩下的就是不能被识别的参数了,但是这种场景就简单了,只需按顺序处理它们即可。
下面是一个用来作验证的例子:
1 #include "../apue.h" 2 int main (int argc, char *argv[]) 3 { 4 int c, i; 5 char fmt[1024] = { 0 }; 6 char *abc = "abcdefghijklmnopqrtsuvwxyz"; 7 char *ABC = "A:B:C:D:E:F:G:H:I:J:K:L:M:N:O:P:Q:R:S:T:U:V:W:X:Y:Z:"; 8 strcat (fmt, abc); 9 strcat (fmt, ABC); 10 while ((c = getopt (argc, argv, fmt)) != -1) { 11 printf ("got option [%d]: '%c' ('%c')", optind, c, optopt); 12 if (optarg) 13 printf (" arg: '%s'", optarg); 14 15 printf ("\n"); 16 } 17 18 printf ("end up at %d\n", optind); 19 if (optind < argc) 20 printf ("some argument left, from %s\n", argv[optind]); 21 exit (1); 22 }
这个例子比较“贪心”,定义了所有的字母做选项,其中小写字母不带参数,大写字母均带参数。最后打印解析不了的参数。
它可以用来验证 getopt 有没有正确的执行:
$ . /getopt -a -b -c -A 1 -B 2 -C 3 admin 123qwe got option [2]: 'a' ( '' ) got option [3]: 'b' ( '' ) got option [4]: 'c' ( '' ) got option [6]: 'A' ( '' ) arg: '1' got option [8]: 'B' ( '' ) arg: '2' got option [10]: 'C' ( '' ) arg: '3' end up at 10 some argument left, from admin |
打印了一些 getopt 相关设施 (optind/optarg/optopt) 的返回值,以便可以观察它们随着选项解析后的变化。
其中中括号中的是 optind 代表的值,表示下一个输入在 argv 中的位置。
当所有选项解析完成后,这个位置将被更新到结尾或第一个参数的位置(如果有)。
我一直有个疑问,如果当参数夹杂在选项中时,这个位置是定位到哪里呢?
如果定位到那个参数的位置,那么应用在向后遍历剩余参数时,岂不是会遍历到已经解析的选项?
如果不是,那岂不是漏掉了一个参数?
于是我用这个小程序做了个测试,就像这样:
$ . /getopt -a -b admin -c -A 1 -B 2 123qwe -C 3 got option [2]: 'a' ( '' ) got option [3]: 'b' ( '' ) got option [5]: 'c' ( '' ) got option [7]: 'A' ( '' ) arg: '1' got option [9]: 'B' ( '' ) arg: '2' got option [12]: 'C' ( '' ) arg: '3' end up at 10 some argument left, from admin |
这次我把用户名参数放在了 -b 与 -c 之间,把密码参数放在了 -B 与 -C 之间。
可以看到,各个选项都解析出来了,没有漏掉;而参数貌似也是正确的。
等等,这个optind显示位置是 argv[10],也就是说 admin 位于 argv[10],但是明明它是 argv[3] 啊!
而且解析完 -C 时 optind 已经到了 12 就是结尾了,怎么最后又倒回去了?
为了解释这种种谜团,在解析完成后加入以下两句代码,打印解析后的命令行:
1 for (i = 0; i<argc; ++ i) 2 printf ("%s ", argv[i]); 3 4 printf ("\n");
新的程序执行输出如下:
$ . /getopt -a -b admin -c -A 1 -B 2 123qwe -C 3 got option [2]: 'a' ( '' ) got option [3]: 'b' ( '' ) got option [5]: 'c' ( '' ) got option [7]: 'A' ( '' ) arg: '1' got option [9]: 'B' ( '' ) arg: '2' got option [12]: 'C' ( '' ) arg: '3' end up at 10 . /getopt -a -b -c -A 1 -B 2 -C 3 admin 123qwe some argument left, from admin |
原来是命令行参数顺序被重新排列了。
所有选项经过解析后排在了参数之前,而参数保持输入时的顺序被排列在选项后面。
这样通过 optind 进行遍历,就会得到原顺序的参数输入,perfect !
通过 man 3 getopt 也发现了这样描述:
By default, getopt() permutes the contents of argv as it scans, so that eventually all the non-options are at the end. |
其它的谜团也迎刃而解。
其实回过头来想,这种 permute argv 参数的成本几乎没有,就是移动几个指针的指向而已,可以说用最小的代价完成了最大的收益。
当然了,getopt 也不是万能的,例如在选项中有重复的输入时,就需要你来处理它们了(不做特别处理的话是后面的选项覆盖前面的)。
本文来自博客园,作者:goodcitizen,转载请注明原文链接:https://www.cnblogs.com/goodcitizen/p/getopt_may_reorder_arguments.html
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)