数据即代码,我和小伙伴们都惊呆了!
几个小伙伴在考虑下面这个各个语言都会遇到的问题:
问题:设计一个命令行参数解析API
一个好的命令行参数解析库一般涉及到这几个常见的方面:
1) 支持方便地生成帮助信息
2) 支持子命令,比如:git包含了push, pull, commit等多种子命令
3) 支持单字符选项、多字符选项、标志选项、参数选项等多种选项和位置参数
4) 支持选项默认值,比如:-port选项若未指定认为5037
5) 支持使用模式,比如:tar命令的-c和-x是互斥选项,属于不同的使用模式
经过一番考察,小伙伴们发现了这个几个有代表性的API设计:
1. getopt():
getopt()是libc的标准函数,很多语言中都能找到它的移植版本。
//C
while ((c = getopt(argc, argv, "ac:d:")) != -1) {
int this_option_optind = optind ? optind : 1;
switch (c) {
case 'a':
printf ("option a");
aopt = 1;
break;
case 'c':
printf ("option c with value '%s'", optarg);
copt = optarg;
break;
case 'd':
printf ("option d with value '%s'", optarg);
dopt = optarg;
break;
case '?':
break;
default:
printf ("?? getopt returned character code 0%o ??", c);
}
}getopt()的核心是一个类似printf的格式字符串的命令行参数描述串,如上面的”ac:d:”定义了”a”, “c”,”d”3个命令行参数,其中,a是一个标志符不需要参数,”c”和”d”需要跟参数。getopt()功能非常弱,只支持单个字符的标志选项和参数选项。如果按上面的5点来比对,基本上只能说是勉强支持第3点,其他几项只能靠程序自己来实现了,所以,想直接基于getopt()实现一个像git这样复杂的命令行参数是不可能的,只有自己来做很多的解析工作。小伙伴们看过getopt()之后一致的评价是:图样图森破。
2. Google gflags
接着,小伙伴们又发现了gflags这个Google出品C++命令行参数解析库。
//C++
DEFINE_bool(memory_pool, false, "If use memory pool");
DEFINE_bool(daemon, true, "If started as daemon");
DEFINE_string(module_id, "", "Server module id");
DEFINE_int32(http_port, 80, "HTTP listen port");
DEFINE_int32(https_port, 443, "HTTPS listen port");
int main(int argc, char** argv) {
::google::ParseCommandLineFlags(&argc, &argv, true);
printf("Server module id: %s", FLAGS_module_id.c_str());
if (FLAGS_daemon) {
printf("Run as daemon: %d", FLAGS_daemon);
}
if (FLAGS_memory_pool) {
printf("Use memory pool: %d", FLAGS_daemon);
}
Server server;
return 0;
}小伙伴们看了后不由得感叹“真心好用啊”!的确,gflags简单地通过几个宏就定义了命令行选项,基本上很好的支持了上面提到的1,3,4这几项,比起getopt()来强多了。对于类似cp这样的小命令,gflags应该是够用了,但要达到git这种级别就显得有些单薄了。
3. Ruby Commander
接下来小伙伴们又发现了Ruby Commander库:
//Ruby
# :name is optional, otherwise uses the basename of this executable
program :name, 'Foo Bar'
program :version, '1.0.0'
program :description, 'Stupid command that prints foo or bar.'
command :bar do |c|
c.syntax = 'foobar bar [options]'
c.description = 'Display bar with optional prefix and suffix'
c.option '--prefix STRING', String, 'Adds a prefix to bar'
c.option '--suffix STRING', String, 'Adds a suffix to bar'
c.action do |args, options|
options.default :prefix => '(', :suffix => ')'
say "#{options.prefix}bar#{options.suffix}"
end
end
$ foobar bar
# => (bar)
$ foobar bar --suffix '}' --prefix '{'
# => {bar}Commander库利用Ruby酷炫的语法定义了一种描述命令行参数的内部DSL,看起来相当高端大气上档次。除了上面的第5项之外,其他几项都有很好的支持,可以说Commander库的设计基本达到了git这种级别命令行参数解析的要求。只是,要搞懂Ruby这么炫的语法和这个库的使用方法恐怕就不如getopt()和gflags容易了。有小伙伴当场表示想要学习Ruby,但是也有小伙伴表示再看看其他库再说。
4. Lisp cmdline库
接下来,小伙伴们发现了Lisp方言Racket的cmdline库。
//Lisp
(parse-command-line "compile" (current-command-line-arguments)
`((once-each
[("-v" "--verbose")
,(lambda (flag) (verbose-mode #t))
("Compile with verbose messages")]
[("-p" "--profile")
,(lambda (flag) (profiling-on #t))
("Compile with profiling")])
(once-any
[("-o" "--optimize-1")
,(lambda (flag) (optimize-level 1))
("Compile with optimization level 1")]
[("--optimize-2")
,(lambda (flag) (optimize-level 2))
(("Compile with optimization level 2,"
"which implies all optimizations of level 1"))])
(multi
[("-l" "--link-flags")
,(lambda (flag lf) (link-flags (cons lf (link-flags))))
("Add a flag <lf> for the linker" "lf")]))
(lambda (flag-accum file) file)
'("filename"))这是神马浮云啊?括号套括号,看起来很厉害的样子,但又不是很明白。看到这样的设计,有的小伙伴连评价都懒得评价了,但也有的小伙伴对Lisp越发崇拜,表示Lisp就是所谓的终极语言了,没有哪门语言能写出这么不明觉历的代码来!小伙伴们正准备打完收工,突然…
5. Node.js的LineParser库
发现了Node.js的LineParser库:
//JavaScript
var meta = {
program : 'adb',
name : 'Android Debug Bridge',
version : '1.0.3',
subcommands : [ 'connect', 'disconnect', 'install' ],
options : {
flags : [
[ 'h', 'help', 'print program usage' ],
[ 'r', 'reinstall', 'reinstall package' ],
[ 'l', 'localhost', 'localhost' ]
],
parameters : [
[ null, 'host', 'adb server hostname or IP address', null ],
[ 'p', 'port', 'adb server port', 5037 ]
]
},
usages : [
[ 'connect', ['host', '[port]'], null, 'connect to adb server', adb_connect ],
[ 'connect', [ 'l' ], null, 'connect to the local adb server', adb_connect ],
[ 'disconnect', null, null, 'disconnect from adb server', adb_disconnect ],
[ 'install', ['r'], ['package'], 'install package', adb_install ],
[ null, ['h'], null, 'help', adb_help ],
]
};
try {
var lineparser = require('lineparser');
var parser = lineparser.init(meta);
// adb_install will be invoked
parser.parse(['install', '-r', '/pkgs/bird.apk']);
}
catch (e) {
console.error(e);
}天啊!?这是什么?我和小伙伴们彻底惊呆了!短短十几行代码就获得了上面5点的全面支持,重要的是小伙伴们居然一下子就看懂了,没有任何的遮遮掩掩和故弄玄虚。本来以为Ruby和Lisp很酷,小伙伴们都想马上去学Ruby和Lisp了,看到这个代码之后怎么感觉前面全是在装呢?有个小伙伴居然激动得哭着表示:我写代码多年,以为再也没有什么代码可以让我感动,没想到这段代码如此精妙,我不由得要赞叹了,实在是太漂亮了!
小伙伴们的故事讲完了,您看懂了吗?如果没有再看一遍标题吧!
建议继续学习:
- 海量数据面试题举例 (阅读:10148)
- 三种东西永远不要放到数据库里 (阅读:7346)
- vim的一个js代码整理的插件jsbeautify.vim (阅读:5565)
- 写代码这件事 (阅读:4672)
- 如何对统计数据进行分析 (阅读:4578)
- 多些时间能少写些代码 (阅读:4297)
- 给你的代码《约法四章》:基本功能、错误处理、智能纠错、日志收集 (阅读:3967)
- 从数据中了解用户——数据在新产品设计中的应用 (阅读:3952)
- 从数据中了解用户——数据在现有产品改版设计中的应用 (阅读:3918)
- 如何为PHP贡献代码 (阅读:3747)
扫一扫订阅我的微信号:IT技术博客大学习
- 作者:Todd 来源: 酷壳 - CoolShell.cn
- 标签: 代码 数据
- 发布时间:2013-08-12 13:52:33
-
[928] WordPress插件开发 -- 在插件使用 -
[134] 解决 nginx 反向代理网页首尾出现神秘字 -
[52] 整理了一份招PHP高级工程师的面试题 -
[52] 如何保证一个程序在单台服务器上只有唯一实例( -
[51] 用 Jquery 模拟 select -
[50] 海量小文件存储 -
[50] Innodb分表太多或者表分区太多,会导致内 -
[50] 全站换域名时利用nginx和javascri -
[49] CloudSMS:免费匿名的云短信 -
[47] jQuery性能优化指南
