`
shangjava
  • 浏览: 1233537 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

如何编写一个完整的Linux命令

 
阅读更多
作者:gzshun. 原创作品,转载请标明出处!
来源:http://blog.csdn.net/gzshun


一个完整的Linux命令需要有以下几个重要的部分组成:
1.使用方法
2.命令行参数
3.移植性

1.使用方法

在每个命令当中,都需要提供一个usage函数,当然名称不一定要用这个。看了很多开源软件,几乎都是使用usage命名。usage一般是在用户输入不规则的命令行参数才调用的,也就是打印出详细的使用方法。比如我以下随便给一个Linux命令传入一个没有被提供的参数,执行结果是这样:


这里每一个命令行选项都代表一个功能,有些选项后面可以跟着参数,比如e2fsck中[-b superblock] [-B blocksize]等等。

2.命令行参数
在第1点,已经贴出usage的打印信息,上面那些-p,-n等等的就是命令行参数,本文也是重点说明这个命令行参数的使用。在执行一个Linux命令的时候,可能只需要增加一个选项(比如: cp -a ...),也可能需要在一个选项后面跟一个参数(比如: e2fsck -C0 或者 e2fsck -C 0),其实"e2fsck -C0"与"e2fsck -C 0"是等价的。这里分析命令行参数的功臣归功于"getopt"函数,getopt()用来分析参数选项,具体可以参考:(UNIX环境高级编程 第21章 与网络打印机通信 p619),当然在UNIX环境高级编程这本书里面,这个函数只是稍微带过,没有详细讲解。

3.移植性
通常一个开源软件的移植性都是考虑得相当周全,在Linux平台下的开源软件,经常被移植到不同的平台,这就必须考虑到不同平台的可移植性。当然我对软件的可移植性这方面的知识欠缺,有待改进,本文不对移植性解释。

一、为什么要写一个完整的Linux命令?
在Linux的平台下,很多重要的功能都是在命令行实现的,也许我们平时也需要自己动手实现一个自己的程序,当然要一个相对比较有质量的。这里就会涉及到一个Linux命令的设计,第一步肯定是要分析命令行参数选项。
自己动手写一个完整的Linux命令,这里的标题写得有点吹,本来我打算写一个相对比较完整的cp命令,但考虑下流程,处理的东西很多,包括文件,目录的属性,还有递归,去掉上班时间,我晚上时间有限,就不想写,那些也都是比较容易的问题。cp这一个命令对于Linux的用户再熟悉不过了,网上也提供很多程序员自己写的cp命令,但我觉得都不完整,因为网上有些cp程序都是直接读取源文件写到目标文件,仅仅写了一个while循环,这样根本就谈不上一个完整的Linux命令。我们平时使用的cp命令,源码有1063行,包括注释,一个简单的功能,其实涉及到很多Linux平台下的特性,需要考虑到文件属性,目录属性,权限,用户和组ID,修改时间,状态修改时间等等。

二、Linux命令的一些例子?
举个例子:
复制一个src目录到dst目录的命令,这里可能会有3种写法:
1.cp -a src dst
2.cp src -a dst
3.cp src dst -a
这3条命令都可以达到将src目录拷贝到dst目录的效果,这里就是命令行参数的功劳了,这也就是getopt函数的好处。如果写一个命令,没有使用getopt函数来处理命令行参数,那将处理不了这三种命令行参数的写法。

例子:
检测磁盘的一个工具e2fsck,该函数有一个选项是[-C fd],后面跟一个参数,这个选项是选择一个不同的进度打印消息。这里同样有几种写法:
1.e2fsck /dev/sda1 -C0
2.e2fsck /dev/sda1 -C 0
在命令行参数,也支持这样的形式,选项可以跟选项后面跟的参数写在一起,也可以分离,但必须紧跟在选项后面。

当然本文也写不出完整的Linux命令,水平有限,只是写出命令行参数的解析部分。

例子:这里是一个简单的测试程序,将程序的参数打印出来;
./cp aa bb cc dd ee -l -d

//这里直接将main函数的argv二位数组打印出来
argv[0]=./cp
argv[1]=aa
argv[2]=bb
argv[3]=cc
argv[4]=dd
argv[5]=ee
argv[6]=-l
argv[7]=-d
//这里是调用完getopt后argv的变化结果
argv[0]=./cp
argv[1]=-l
argv[2]=-d
argv[3]=aa
argv[4]=bb
argv[5]=cc
argv[6]=dd
argv[7]=ee

从这个例子,可以得到以下结果:
1.getopt函数具有排序功能;
2.argv是一个可以修改的二维数组;

三、getopt的使用?
1.排序功能
getopt会先将命令行参数进行排序,选项在前,剩下的参数在后面,第0个参数始终是程序自己;
排序规则:
只将有"-"选项与该选项的参数提到程序的后面,剩下的一些都放在最后面,但原有的参数顺序没有更改,这里有点拗口,直接看例子:
cp -a -r src1/ src2/ src3/ dst/
这条命令的任务是将src1,src2,src3目录拷贝到dst目录下,src1,src2,src3参数必须在dst参数的前面,所以getopt不会破坏原来的参数顺序,例子:
cp src1/ src2/ -a src3/ -r dst/
经过getopt的处理,argv数组将会变成cp -a -r src1/ src2/ src3/ dst/,-a和-r原有的顺序也保持不变,-a在前,-r在后。

2.参数解析
有些命令的选项后面会跟着一个参数,可以是数字,也可以是字母,或字符串,均可。例子:
e2fsck /dev/sda1 -C0,这里-C选项的参数是0。也可以写成e2fsck /dev/sda1 -C 0,跟前面那句命令等价。

3.getopt的具体用法
讲这么废话,终于到了getopt了。
getopt函数声明在unistd.h头文件中,有以下一些相关函数和全局变量:


这里写个例子来说明(虚拟的): mycp -a -b hello -c123 src dst
char *optarg: -b的optarg是hello,-c的optarg是123,optarg就是选项后面跟着的参数;
int optind : 下一个要处理的参数的下标(argv);
int opterr : 如果将opterr设为0,则getopt不输出错误信息,否则报错,形如(e2fsck: invalid option -- 'x')
int optopt : 当命令行选项字符不包括在optstring中或者选项缺少必要的参数时,该选项存储在optopt中
int getopt(int argc, char * const argv[], const char *optstring) : optstring看起来比较不清楚,待会儿看代码。该函数执行完或者失败返回-1.

源程序:


程序的执行结果1:
这里故意打乱顺序,为了更清楚的查看getopt的使用方法


程序的执行结果2:
使用-h选项来打印命令的使用方法


个人喜欢vi的颜色配置,贴一张出来看看,挺鲜艳:



本人不才,以上可能存在错误的认识。
由于最近写了3个Linux命令,在CSDN博客总结一下收获,虽然这种程序是菜鸟级的,但我把它记录下来,等我下次要查看,一目了然,这就是效率。希望大牛不要鄙视,我写这种无聊的程序,只是一个学习态度罢了。

分享到:
评论
Global site tag (gtag.js) - Google Analytics