如何編寫一個完整的Linux命令

來源:互聯網
上載者:User

作者:gzshun. 原創作品,轉載請標明出處!
來源:http://blog.csdn.net/gzshun

一個完整的Linux命令需要有以下幾個重要的部分組成:
1.使用方法
2.命令列參數
3.移植性

1.使用方法

在每個命令當中,都需要提供一個usage函數,當然名稱不一定要用這個。看了很多開源軟體,幾乎都是使用usage命名。usage一般是在使用者輸入不規則的命令列參數才調用的,也就是列印出詳細的使用方法。比如我以下隨便給一個Linux命令傳入一個沒有被提供的參數,執行結果是這樣:

gzshun@ubuntu:~/c/getopt$ e2fsck -xe2fsck: 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] deviceEmergency 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 dstgetopt()用來分析參數選項,具體可以參考:(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 helloBefore calling getopt./getopt china -a beijing -b shanghai -c shenzhen -d -f 1234 -g hello-f optarg=1234-g optarg=helloAfter 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 nooptind=9        : argv[9]=chinaoptind=10       : argv[10]=beijingoptind=11       : argv[11]=shanghaioptind=12       : argv[12]=shenzhen

程式的執行結果2:

使用-h選項來列印命令的使用方法

gzshun@ubuntu:~/c/getopt$ ./getopt -hBefore calling getopt./getopt -hUsage:  getopt [OPTION]        [-abcdeh]        [-f digit] [-g string]

個人喜歡vi的顏色配置,貼一張出來看看,挺鮮豔:

本人不才,以上可能存在錯誤的認識。
由於最近寫了3個Linux命令,在CSDN部落格總結一下收穫,雖然這種程式是菜鳥級的,但我把它記錄下來,等我下次要查看,一目瞭然,這就是效率。希望大牛不要鄙視,我寫這種無聊的程式,只是一個學習態度罷了。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.