如何编写一个完整的Linux命令
来源:互联网 发布:大数据综合服务平台 编辑:程序博客网 时间:2024/05/21 09:19
一个完整的Linux命令需要有以下几个重要的部分组成:
1.使用方法
2.命令行参数
3.移植性
1.使用方法
在每个命令当中,都需要提供一个usage函数,当然名称不一定要用这个。看了很多开源软件,几乎都是使用usage命名。usage一般是在用户输入不规则的命令行参数才调用的,也就是打印出详细的使用方法。比如我以下随便给一个Linux命令传入一个没有被提供的参数,执行结果是这样:
- gzshun@ubuntu:~/c/getopt$ e2fsck -x
- e2fsck: invalid option -- 'x'
- Usage: e2fsck [-panyrcdfvtDFV] [-b superblock] [-B blocksize]
- [-I inode_buffer_blocks] [-P process_inode_size]
- [-l|-L bad_blocks_file] [-C fd] [-j external_journal]
- [-E extended-options] device
- Emergency help:
- -p Automatic repair (no questions)
- -n Make no changes to the filesystem
- -y Assume "yes" to all questions
- -c Check for bad blocks and add them to the badblock list
- -f Force checking even if filesystem is marked clean
- -v Be verbose
- -b superblock Use alternative superblock
- -B blocksize Force blocksize when looking for superblock
- -j external_journal Set location of the external journal
- -l bad_blocks_file Add to badblocks list
- -L bad_blocks_file Set badblocks list
这里每一个命令行选项都代表一个功能,有些选项后面可以跟着参数,比如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头文件中,有以下一些相关函数和全局变量:
- #include <unistd.h>
- extern char *optarg;
- extern int optind;
- extern int opterr;
- extern int optopt;
- extern int getopt(int argc, char * const argv[], const char *optstring);
这里写个例子来说明(虚拟的): 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.
源程序:
- /*****************************************************
- ** Name : getopt.c
- ** Author : gzshun
- ** Version : 1.0
- ** Date : 2012-01
- ** Description : getopt test program
- **
- ** This file may be redistributed under the terms
- ** of the GNU Public License.
- ******************************************************/
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- extern char *optarg;
- extern int optind;
- extern int opterr;
- extern int optopt;
- extern int getopt(int argc, char * const argv[], const char *optstring);
- #define OPTION_A (1<<0)
- #define OPTION_B (1<<1)
- #define OPTION_C (1<<2)
- #define OPTION_D (1<<3)
- #define OPTION_E (1<<4)
- #define OPTION_F (1<<5)
- #define OPTION_G (1<<6)
- #define OPTION_H (1<<7)
- const char *programe_name = "getopt";
- static void usage(void);
- static int PRS(int argc, char **argv, int *opts);
- static void usage(void)
- {
- fprintf(stderr,
- "Usage: %s [OPTION]\n"
- "\t[-abcdeh]\n"
- "\t[-f digit] [-g string]\n",
- programe_name);
- exit(1);
- }
- static int PRS(int argc, char **argv, int *opts)
- {
- int retval, prog_num;
- char ch;
- *opts &= 0x00000000;
- /*******************************************************
- optstring: "abcdef:g:h"
- 这个字符串是给getopt指定需要的选项.
- abcdeh : 这几个选项后面没有跟选项参数
- f:g: : 在一个选项的后面多一个:号,说明这个选项后面是有参数的.
- 这个选项参数可以通过optarg全局变量获取到.
- *******************************************************/
- while ((ch = getopt(argc, argv, "abcdef:g:h")) != -1) {
- switch (ch) {
- case 'a':
- *opts |= OPTION_A;
- break;
- case 'b':
- *opts |= OPTION_B;
- break;
- case 'c':
- *opts |= OPTION_C;
- break;
- case 'd':
- *opts |= OPTION_D;
- break;
- case 'e':
- *opts |= OPTION_E;
- break;
- case 'f':
- *opts |= OPTION_F;
- printf("-f optarg=%s\n", optarg);
- break;
- case 'g':
- *opts |= OPTION_G;
- printf("-g optarg=%s\n", optarg);
- break;
- case 'h':
- *opts |= OPTION_H;
- usage();
- break;
- default:
- fprintf(stderr,
- "our: %s: invalid option -- '%c'\n",
- programe_name, optopt);
- exit(1);
- break;
- }
- }
- return 0;
- }
- int main(int argc ,char **argv)
- {
- int i, retval;
- int opts;
- /*调用getopt之前打印argv数组*/
- printf("Before calling getopt\n");
- for (i = 0; i < argc; i++) {
- printf("%s ", argv[i]);
- }
- printf("\n\n");
- /**********************************************************
- 该函数是编写一个Linux命令最基本也最重要的一个操作,函数里面
- 调用getopt()函数,函数作用是用来解析命令的操作选项:
- 比如:cp -a -r src dst
- getopt()用来分析参数选项,具体可以参考:
- (UNIX环境高级编程 第21章 与网络打印机通信 p619)
- ***********************************************************/
- retval = PRS(argc, argv, &opts);
- if (retval < 0) {
- exit(1);
- }
- printf("\n");
- /*调用getopt之后打印argv数组*/
- printf("After calling getopt\n");
- for (i = 0; i < argc; i++) {
- printf("%s ", argv[i]);
- }
- printf("\n\n");
- /*这里来打印一下选项的设置情况*/
- printf("-a \t set %s\n", opts&OPTION_A ? "yes" : "no");
- printf("-b \t set %s\n", opts&OPTION_B ? "yes" : "no");
- printf("-c \t set %s\n", opts&OPTION_C ? "yes" : "no");
- printf("-d \t set %s\n", opts&OPTION_D ? "yes" : "no");
- printf("-e \t set %s\n", opts&OPTION_E ? "yes" : "no");
- printf("-f \t set %s\n", opts&OPTION_F ? "yes" : "no");
- printf("-g \t set %s\n", opts&OPTION_G ? "yes" : "no");
- printf("-h \t set %s\n", opts&OPTION_H ? "yes" : "no");
- printf("\n");
- /**********************************************************
- 当执行完getopt后,argv二位数组里面的字符串已经被排序,选项部分
- 会被排在前面,非选项部分的会被排在最后面,但原来的顺序还是没有
- 发生变化;
- 为什么要加这个循环,因为在很多情况都要另外加一些参数,比如:
- cp -a -r src1 src2 src3 src4 dst
- 这条复制命令, -a -r是选项, src[1-4] dst这几个就是其余的参数,
- 若需要这些,使用以下循环即可获得.
- ***********************************************************/
- for (i = optind; i < argc; i++) {
- printf("optind=%d\t: argv[%d]=%s\n", i, i, argv[i]);
- }
- return 0;
- }
程序的执行结果1:
这里故意打乱顺序,为了更清楚的查看getopt的使用方法
- gzshun@ubuntu:~/c/getopt$ ./getopt china -a beijing -b shanghai -c shenzhen -d -f 1234 -g hello
- Before calling getopt
- ./getopt china -a beijing -b shanghai -c shenzhen -d -f 1234 -g hello
- -f optarg=1234
- -g optarg=hello
- After calling getopt
- ./getopt -a -b -c -d -f 1234 -g hello china beijing shanghai shenzhen
- -a set yes
- -b set yes
- -c set yes
- -d set yes
- -e set no
- -f set yes
- -g set yes
- -h set no
- optind=9 : argv[9]=china
- optind=10 : argv[10]=beijing
- optind=11 : argv[11]=shanghai
- optind=12 : argv[12]=shenzhen
程序的执行结果2:
使用-h选项来打印命令的使用方法
- gzshun@ubuntu:~/c/getopt$ ./getopt -h
- Before calling getopt
- ./getopt -h
- Usage: getopt [OPTION]
- [-abcdeh]
- [-f digit] [-g string]
个人喜欢vi的颜色配置,贴一张出来看看,挺鲜艳:
本人不才,以上可能存在错误的认识。
由于最近写了3个Linux命令,在CSDN博客总结一下收获,虽然这种程序是菜鸟级的,但我把它记录下来,等我下次要查看,一目了然,这就是效率。希望大牛不要鄙视,我写这种无聊的程序,只是一个学习态度罢了。
- 如何编写一个完整的Linux命令
- 如何编写一个完整的Linux命令
- 如何编写一个完整的Linux命令
- linux 下如何编写一个完整的应用程序!
- 如何编写一个完整全面的测试用例
- 如何编写Linux下的cat命令?
- 用VC++6.0 编写一个完整的
- 用VC++6.0 编写一个完整的
- 模拟linux的命令wc编写的一个函数
- 如何编写一个可靠的linux守护进程
- 如何编写一个可靠的linux守护进程
- 如何编写一个可靠的linux守护进程
- 如何定义一个完整的类
- 如何构建一个完整的WEB服务器
- 如何定义一个完整的类
- 如何建立一个完整的游戏AI
- 如何读一个完整的大数据
- 如何编写一个Linux字符设备驱动?
- scribe、chukwa、kafka、flume日志系统对比
- 热更新
- NIO学习系列笔记(2)
- 基于JQuery 改造bootstrap模态框拖动功能
- android内存泄漏分析
- 如何编写一个完整的Linux命令
- C++数据结构: 顺序表 详细实现
- UILable响应点击事件
- 一个特殊的内码转换处理对于XML格式的UTF-8
- WorkFlow简单使用
- 体验几个超酷超炫的HTML 5 Cavas应用
- 使用python进行数据分析
- 数据结构之链表与数组(-)——数组和链表的简介
- C++中#include<string>与#include<string.h>的区别