天天看點

linux中more指令啟動程式,linux下more指令的實作

本文隻是學渣作者的學習經曆,能幫到讀者是作者的榮幸。功能沒有實作完全,希望各位多多提意見。

more指令基本功能實作:‘q’--退出,空格--下一頁,Enter鍵--下一行,清螢幕顯示,關閉回顯,執行指令無需按鍵enter.

支援多種用法:         more file    ls /bin/ | more      more < file

在實作ls /bin/ | more 時,需要将顯示輸入和使用者指令輸入分開,即打開/dev/tty來實作指令輸入。

不足:1.在每次空格或回車後,"more?"都會顯示出來

2.沒有顯示百分數

3.沒有對檔案類型進行判斷--可以通過magic number實作

1)讀寫函數的用法---在實作more指令時的總結

fopen\fclose

1.FILE *fopen(const char *filename, const char *mode)

fopen打開有filename指定的檔案,并把它與一個檔案流關聯起來。

成功打開,傳回一個非空FILE *指針,失敗傳回NULL。

filename可以是某個text檔案,也可以是某個裝置名,比如/dev/tty

mode 是打開模式,可以是 r--隻讀。w--寫方式,新内容覆寫就的内容。a--寫方式,新内容追加在檔案尾。a+ -- 更新的方式打開,追加。其它。

2. int fclose(FILE *stream)

關閉指定的檔案流stream

fread\fwrite

1. size_t fread(void *ptr, size_t size, size_t nitems, FILE *stream)

fread 将檔案流中的資料讀到ptr所指空間中。

fread(buf,sizeof(char),strlen(buf),stream);

buf是空閑記憶體空間,一般是 char buf[1024]

sizeof(char)是一個char類型的大小,也是fread一次讀取的大小

strlen(buf)是讀取的次數,一般是數組長度。

stream是fopen傳回的非空檔案流指針。

讀取與stream關聯的檔案,每次讀sizeof(char)大小,讀strlen(buf)次。

fread 傳回的是成功讀到資料緩沖區裡的記錄個數。這裡是strlen(buf)個記錄。

讀到buf中的資料,系統自動在資料最後添加一個“\n”

2.size_t fwrite(const void *ptr,size_t size,size_t nitems, FILE *stream)

fwrite将ptr所指空間的内容寫到檔案流中。

用法與fread相似

fgets\fputs

1. char *fgets(char *s,int n,FILE *stream)

fgets把讀到的字元寫到s指向的字元串裡,知道出現以下情況;遇到換行符,已經傳輸了n-1個字元,或者到達檔案尾。

成功完成,傳回一個指向字元串s的指針; 出錯,傳回一個空指針; 到達檔案尾,fgets會設定這個檔案流的EOF辨別,并傳回一個空指針。

2. int fputs(const char *s, FILE *stream)

fputs将s指向字元串寫到檔案流stream中(不自動寫入字元串結束标記符'\0')。

成功完成,傳回非負數; 失敗傳回EOF。

fseek----本執行個體中沒有用到 ,但通過它讀取檔案magic number來實作對檔案類型的判斷

1. int fseek(FILE *stream,long int offset, int whence)

fseek在檔案流裡為下一次讀寫操作指定位置。

offset--指定位置,與whence聯用

whence取值:

SEEK_SET --- 表明offset是相對于檔案頭的一個相對值,則從offset處讀取資料(其實是相對于檔案頭的)

SEEK_CUR --- 表明offset是相對于目前位置的一個相對值,則從目前位置偏移offset出讀取資料

SEEK_END --- 表明offset是相對于檔案尾的一個相對值,則從距檔案尾offset處開始讀取資料。特别注意:此處的offset是負值

fseek 傳回0表示成功 ,傳回-1表示失敗,并設定errno指出錯誤。

memset

1. void *memset(void *s, int ch, size_t n)

在一段記憶體中填充ch,以達到對較大結構體或數組進行初始化(清零操作)。

試驗案例:在聲明一個字元數組後 char buf[1024],系統會随機給buf指派,如果沒有進行memset(buf,0,1024)操作,則在進行fread之後,       用printf輸出buf時會有亂碼。

2)終端控制

(1)利用終端結構體termios實作回顯關閉,非标準輸入行處理設 置

termios可對終端接口進行控制,termios資料結構和相關函數調用定義在termios.h中

可以被調整來影響終端的值按照不同的模式被分成如下幾組:輸入模式,輸出模式,控制模式 ,本地模式,特殊控制字元。

在本例中用到了本地模式,特殊控制字元,以下是運作本例的前期知識準備:

最小的termios結構的典型定義如下:

回顯功能關閉:         c_lflag &= ~ECHO

非标準輸入行處理: c_lflag &= ~ICANON;                                                  c_cc[VMIN] = 1;c_cc[VTIME] = 0

涉及到的函數原型:

int tcgetattr(int fd, struct termios *termios_p)

int tcsetattr(int fd,int actions,const struct termios *termios_p)

參數清單中:fd為檔案流的檔案描述符,可以通過函數fileno(FILE *)來得到。

(2)利用terminfo軟體包完成清屏,光标定位。

terminfo使程式不必去迎合多變的終端類型,其隻要通過查詢終端類型資料庫來找到正确的終端資訊。

在多數現代UNIX系統(包括linux)中,這個軟體包和另一個軟體包curses內建在一起。

為了使用terminfo函數,通常需要包括curses頭檔案curses.h和terminfo自己的頭檔案term.h。

在本例中,我們利用terminfo擷取螢幕資訊,實作了清屏和光标定位的功能。

涉及到的函數原型為:

1. int setuptterm(char *term, int fd, int *errret);

設定終端類型,為目前的終端類型初始化一個TERMINAL結構。

char *term為NULL是則使用環境變量TERM值(其實我們的目的就是擷取目前終端類型的TERMINAL結構體,是以此項一般設定為NULL)

2. int tigetnum(char *capname);

通過capname擷取數值型terminfo資料項,如終端顯示的最大行數,最大列數等數值型資料項。

3.char *tigetstr(char *capname);

通過capname擷取字元型terminfo資料項,如清屏的指令字元串,光标移動的指令字元串,其是參數化字元串,即還需要通過tparm函數來輸入具體的光标位置。

4.char *tparm(char *cap,long p1,long p2,..., long p9);

同過tparm函數實際的數值替換功能替換指令字元串中的參數,如光标移動的指令字元串。

5.int putp(char *const str);

putp将指令字元串發到終端,其針對的是标準輸出流。如果不能通過标準輸出stdout通路終端,則需要 tputs(char *const str,int affcnt, int (*putfunc)(int))來

指定一個用 于輸出  字元的函數putfunc, putp(string)相當于tputs(string,1,putchar),int affcnt一般置為1。

(3)對于終端的控制還可以用ncurses庫函數實作。

Referrences:

《Unix/Linux程式設計實踐教程》

《Linux程式設計》 Edition 4

代碼實作:(站在巨人的肩膀上)

運作平台:CentOS6.4 GCC 4.4.7

#include

#include

#include

#include

#include

typedef int Status;

int LINELEN,PAGELEN; //全局變量,在GetTermInfo中獲得

Status DoMore(FILE *);

Status SeeMore(FILE *);

Status EchoSet(struct termios *,FILE *);

Status EchoBack(struct termios *,FILE *);

Status GetTermInfo();

Status DoMore(FILE *fp)

{

struct termios *initialrsettings,*newrsettings;

initialrsettings = (struct termios *)malloc(sizeof(struct termios));

newrsettings = (struct termios *)malloc(sizeof(struct termios));

char line[LINELEN];

int num_of_lines = 0;

int user_action;

FILE *fp_tty;

fp_tty = fopen("/dev/tty","r");

if(NULL == fp_tty)

{

exit(1);

}

tcgetattr(fileno(fp_tty),initialrsettings);

*newrsettings = *initialrsettings;//此處不要用指針指派,否則無法恢複初始設定.

while(fgets(line,LINELEN,fp))

{

if(num_of_lines == PAGELEN)

{

EchoSet(newrsettings,fp_tty);

user_action = SeeMore(fp_tty);

if(user_action==0)

{

EchoBack(initialrsettings,fp_tty); //恢複終端初始設定

printf("\n");

break;

}

num_of_lines = num_of_lines - user_action;

}

if(fputs(line,stdout)==EOF)

{

exit(1);

}

num_of_lines++;

}

EchoBack(initialrsettings,fp_tty);//恢複終端初始設定

}

int SeeMore(FILE *cmd)

{

char c;

printf("\033[7m more? \033[m");

while((c=fgetc(cmd))!=EOF)

{

if(c == 'q')

{

return 0;

}

if(c == ' ')

{

return PAGELEN;

}

if(c == '\n')

{

return 1;

}

}

return 0;

}

Status EchoSet(struct termios *newrsettings,FILE *fp_tty)

{

if(NULL == newrsettings||NULL == fp_tty)

{

return 1;

}

(*newrsettings).c_lflag &= ~ECHO;//關閉回顯

(*newrsettings).c_lflag &= ~ICANON;//以下三行--無需輸入回車即可執行使用者指令

(*newrsettings).c_cc[VMIN] = 1;

(*newrsettings).c_cc[VTIME] = 0;

tcsetattr(fileno(fp_tty),TCSAFLUSH,newrsettings);

//free(newrsettings); //運作此行代碼出錯,因為 tcsetattr調用完畢後自動free.

//newrsettings = NULL;

return 0;

}

Status EchoBack(struct termios *initialrsettings,FILE *fp_tty)

{

if(NULL == initialrsettings||NULL == fp_tty)

{

return 1;

}

tcsetattr(fileno(fp_tty),TCSANOW,initialrsettings);

return 0;

}

Status GetTermInfo()

{

char *clear,*cursor;

setupterm(NULL,fileno(stdout),(int *)0);

clear = (char *)tigetstr("clear");

cursor = (char *)tigetstr("cup");

PAGELEN = tigetnum("lines")-1;

LINELEN = tigetnum("cols");

putp(clear);

putp(tparm(cursor,0,0));

return 0;

}

int main(int ac,char *av[])

{

FILE *fp;

if(ac == 1)

{

GetTermInfo();

DoMore(stdin);

}else

{

while(--ac)

{

if((fp=fopen(*++av,"r"))!=NULL)

{

GetTermInfo();

DoMore(fp);

fclose(fp);

}else

{

exit(1);

}

}

}

return 0;

}