關於cp命令的編寫
婁老師在課上詳細的講了命令who的編寫過程~對此,我很有啟發!於是想親自動手試試~ 有什么不足的地方請大家提出來!
Learning by doing ~ 做中學,真的只有自己動手去實踐一遍,才能體會到其中的精髓!我跟着婁老師講who命令時的思路來做了一遍cp,收獲頗多~很爽!
參考資料——別出心材的Linux系統調用
在編寫cp命令之前,同樣有三個問題:
- cp命令能做什么
- cp命令是如何實現的?
- 能不能自己編寫一個cp命令?
問題1. cp命令能做什么?
cp
能夠復制文件,典型的用法是:
cp source-file target-file
如果target-file所指定的文件不存在,cp就創建這個文件,如果已經存在就覆蓋,target-file的內容與source-file相同。
問題2. cp命令是如何實現的?
一個Linux命令的功能我們可以通過whatis
或man -f
來查看,當然最好的方法是自己通過使用來體驗一下:
要進一步了解cp的用法,需要借助聯機幫助manpages。我們輸入man -k cp,再輸入man 1 cp。
要學會在幫助文檔中尋找有用的信息!
到這,即使以前沒有用過cp命令,也大概知道它的功能了。
了解cp的功能后,寫出其偽代碼:
- 打開source-file
- 創建target-file
- 從source-file讀出一段數據
- 把這段數據寫入target-file
- 關閉source-file
- 關閉target-file
現在的問題是如何創建文件?如何打開文件?如何讀文件,如何寫文件?
如何創建文件:
如何讀文件:
我們用man -k read | grep file | grep 2
搜一下,再man 2 read
如何打開文件:
圖片中紅線內部是需要用到的庫。
然后使用grep -nr XXX /usr/include
查找相關宏。
如何寫文件:
問題3.能不能自己編寫一個cp命令
看代碼的時候我最先不理解的就是main函數的定義:
int main(int argc, char *argv[]){}
經查閱得知,argc是用來表示在命令行下輸入命令時的參數個數,包括指令本身;argv[]是用來取得你輸入的參數。針對具體指令分析如下(每一步解釋由注釋形式給出)。
首先先先看一下涉及到的頭文件的用處:
- stdio.h 標准輸入輸出
- stdlib.h C標准函數庫
- unistd.h Unix類系統定義符號常量
- fcntl.h 定義了很多宏和open,fcntl函數原型
- sys/types.h 基本系統數據類型
- dirent.h unix類目錄操作的頭文件,包含了許多UNIX系統服務的函數原型,例如opendir函數、readdir函數。
- termios.h 在Posix規范中定義的標准接口
代碼如下:
#include <stdio.h>//標准輸入輸出
#include <stdlib.h>//C標准函數庫
#include <unistd.h>//Unix類系統定義符號常量
#include <fcntl.h>//定義了很多宏和open,fcntl函數原型
#define BUFFERSIZE 4096//定義存儲器容量
#define COPYMODE 0644//定義復制的長度
void oops(char *, char *);
int main(int argc, char *argv[])
{
int in_fd, out_fd, n_chars;//三個描述符值
char buf[BUFFERSIZE];//存儲器位置
/*cp的參數有兩個,分別是要復制的文件,和目的目錄,這樣一共應該是有三個操作數
所以要先檢查argc的值是否為三,如果不是,返回標准錯誤*/
if (argc != 3) {
fprintf(stderr, "usage: %s source destination\n", *argv);
exit(1);
}
/*檢查cp的第一個參數,要復制的文件,用open打開,in_fd為open返回的描述符
如果返回-1,代表打開失敗,提示錯誤*/
if ((in_fd = open(argv[1], O_RDONLY)) == -1)
oops("Cannot open ", argv[1]);
/*檢查cp的第二個參數,復制的目的地址,用create在目的地址創建新文件,out_fd為open返回的描述符
如果返回-1,代表創建失敗,提示錯誤*/
if ((out_fd = creat(argv[2], COPYMODE)) == -1)
oops("Cannot creat", argv[2]);
/*cp指令的動作就是讀取一個文件的內容到存儲器,在新的地址創建空白文件,再從存儲器將內容寫入新文件。
這里判斷復制是否成功:
如果能讀取順利,而讀取的位數和寫的位數不同,是寫錯誤;
如果讀取失敗,是讀錯誤。*/
while ((n_chars = read(in_fd, buf, BUFFERSIZE)) > 0)
if (write(out_fd, buf, n_chars) != n_chars)
oops("Write error to ", argv[2]);
if (n_chars == -1)
oops("Read error from ", argv[1]);
/*這里執行的是關閉文件的動作,in_fd和out_fd兩個文件描述符
所指向的文件只要有一個關閉錯誤,就提示關閉錯誤。*/
if (close(in_fd) == -1 || close(out_fd) == -1)
oops("Error closing files", "");
}
/*這個是用來輸出錯誤信息的函數*/
void oops(char *s1, char *s2)
{
fprintf(stderr, "Error: %s ", s1);
perror(s2);//用來將上一個函數發生錯誤的原因輸出到標准設備(stderr)
exit(1);
}
這里要注意main函數的兩個參數:
- argc記錄了用戶在運行程序的命令行中輸入的參數的個數。
- arg[]指向的數組中至少有一個字符指針,即arg[0].它通常指向程序中的可執行文件的文件名。
代碼驗證:
首先新建兩個txt文件,我這里為1.txt和2.txt。
1.txt里面的內容是111111;2.txt的內容為222222.
經過cp命令后,1.txt的內容拷貝到了2.txt里面。
將2.txt里面的內容復原后,用編寫的cp命令來試一下,
果然經過編寫的cp命令后,1.txt的內容拷貝到了2.txt里面。
說明編寫的cp代碼正確!
總結
通過上面的例子,我們學習了Linux中學習Linxu系統編程的方法:
- 仔細研究manpages
- 問題驅動,使用man -k key1|grep key2|...在manpages中搜索你要的內容
- 閱讀.h文件: 可以通過grep -nr XXXX /usr/incldue查找相關的宏定義,結構體定義,類型定義等
- 解決一個問題要多個系統調用,可以參考manpages的SEE ALSO部分來得到相關系統調用的信息