自作のtail(その9)

気づけば(その9)まで来てしまいました。今回でtailっぽいことはできるようになりましたので、一応完成です。でも、まだまだ機能を追加していくので、タイトルを変えて続けると思います。

さて、本物版tailですが、特殊なオプション指定ができますよね。

tail -行数

このようにハイフンに続いて数字を指定する方法。まあ、普段から使ってるので、普通の方法と言えますが。

使ってみます。

takk@deb9:~/tmp$ seq 10 >a
takk@deb9:~/tmp$ tail -3 a
8
9
10
takk@deb9:~/tmp$ 

普段使っていて気づかなかったのですが、使い方に制限があります。

  • -行数 オプションは第1パラメータであること
  • 他のオプションが指定されていないこと
  • 指定できるファイルは一つ
  • 作りを簡単にするような制限に思えます。-行数オプションの指定方法を間違えると、このようなエラーになります。

    takk@deb9:~/tmp$ seq 10 >a
    takk@deb9:~/tmp$ seq 21 30 >b
    takk@deb9:~/tmp$ tail a -3
    tail: 無効なコンテキストでオプションが使用されました -- 3
    takk@deb9:~/tmp$ tail -v -3 a
    tail: 無効なコンテキストでオプションが使用されました -- 3
    takk@deb9:~/tmp$ tail -3 -v a
    tail: 無効なコンテキストでオプションが使用されました -- 3
    takk@deb9:~/tmp$ tail -3 a b
    tail: 無効なコンテキストでオプションが使用されました -- 3
    takk@deb9:~/tmp$ 
    

    では、-行数 オプション処理を考えてみます。
    getoptだけでは、-だけのオプションは処理できないので、getoptでオプション解析する前に、-行数 オプション解析を行います。-行数 オプションの判定は、atoiで変換した値が0より大きな数値になるかどうかで、判定しています。
    ネストが深くならないようにgoto抜けしてます。

         1	#include <stdio.h>
         2	#include <stdlib.h>
         3	#include <getopt.h>	//getopt
         4	
         5	int main(int argc, char* argv[])
         6	{
         7		int optch;
         8		int tmp;
         9	
        10	
        11		if(argc==3 && *argv[1]=='-'){
        12			tmp = atoi(argv[1]+1);
        13			if(tmp > 0)
        14				goto getopt_skip;	
        15		}
        16	
        17		while ((optch = getopt(argc, argv, "n:")) != -1){
        18			switch (optch) {
        19			case 'n':   
        20				tmp = atoi(optarg);
        21				break;
        22			default:
        23				printf("parameter error\n");
        24				return 1;
        25			}
        26		}
        27	getopt_skip:
        28	
        29		printf("-n = %d\n",tmp);
        30		return 0;
        31	}
    

    gccでビルド後、確認してみます。

    takk@deb9:~/tmp$ ./a.out -5 a
    -n = 5
    takk@deb9:~/tmp$ ./a.out -n6 a
    -n = 6
    takk@deb9:~/tmp$ ./a.out -5 a b
    ./a.out: invalid option -- '5'
    parameter error
    takk@deb9:~/tmp$ ./a.out -n6 a b
    -n = 6
    takk@deb9:~/tmp$ 
    

    上手く解析できてそうなので、この判定方法で作っていきます。

    さて、前回までのプログラムに、オプション解析と複数ファイル処理、また、行単位、Byte単位の抽出機能すべてを盛り込んだプログラムがこれです。

    takk@deb9:~/tmp$ cat -n t.c
         1	#include <stdio.h>
         2	#include <sys/types.h>	//stat
         3	#include <sys/stat.h>	//stat
         4	#include <unistd.h>	//stat
         5	#include <getopt.h>	//getopt
         6	#include <stdlib.h>	//atoi
         7	#include <fcntl.h>	//open
         8	
         9	int get_tailsize(int fd,int filesize,int req_lines)
        10	{
        11		char buf[10],*bufp;
        12		int bsize=sizeof(buf);
        13		int i,size,bno,this_size,lf_num=0;
        14	
        15		for(bno=0; bno <= filesize/bsize; bno++){
        16			size = bsize*(bno+1);
        17			size = filesize < size ? filesize : size;
        18			lseek(fd, -size, SEEK_END);
        19	
        20			size = size % bsize;
        21			size = size > 0 ? size : bsize;
        22			bufp = buf;
        23			while(1){
        24				this_size = read(fd, bufp, size - (bufp-buf));
        25				if(this_size == 0)break;
        26				bufp += this_size;
        27			}
        28	
        29			for(i = 0; i < size; i++){
        30				if(buf[size-i-1] == 0x0a){
        31					if(!((bno == 0) && (i==0))){
        32						lf_num++;
        33					}
        34					if(lf_num == req_lines){
        35						return bsize*bno+i;
        36					}
        37				}
        38			}
        39		}
        40		return filesize;
        41	}
        42	
        43	void out_taillines(int fd,int size)
        44	{
        45		char buf[10],*bufp;
        46		int bsize=sizeof(buf);
        47		int read_size,this_size;
        48	
        49		lseek(fd, -size, SEEK_END);
        50		
        51		while(1){
        52			bufp = buf;
        53			read_size = bsize <= size ? bsize : size % bsize;
        54	
        55			this_size = read(fd, buf, read_size);
        56			size -= this_size;
        57			while(this_size--){
        58				putchar((int)*bufp++);
        59			}
        60			if(size <= 0)
        61				break;
        62		}
        63	}
        64	
        65	int main(int argc, char* argv[])
        66	{
        67		int fd;
        68		char *fname;
        69		struct stat fs;
        70		int optch;
        71		int qflag = 0;
        72		int req_lines = 10;
        73		int csize = 0;
        74		int size;
        75	
        76		if(argc==3 && *argv[1]=='-'){
        77			req_lines = atoi(argv[1]+1);
        78			if(req_lines > 0){
        79				argc-=2;
        80				argv+=2;
        81				goto getopt_skip;	
        82			}
        83		}
        84	
        85		while ((optch = getopt(argc, argv, "n:c:q")) != -1){
        86			switch (optch) {
        87			case 'n':   
        88				req_lines = atoi(optarg);
        89				break;
        90			case 'c':   
        91				csize = atoi(optarg);
        92				break;
        93			case 'q':   
        94				qflag = 1;
        95				break;
        96			default:
        97				printf("parameter error\n");
        98				return 1;
        99			}
       100		}
       101	
       102		argv += optind;
       103		argc -= optind;
       104	
       105	getopt_skip:
       106		if(argc==1)
       107			qflag = 1;
       108	
       109		while(argc--){
       110			fname = *argv++;
       111	
       112			stat(fname, &fs);
       113	
       114			if((fd=open(fname, O_RDONLY)) != -1){
       115				if(csize)
       116					size = fs.st_size < csize ? fs.st_size : csize;
       117				else
       118					size = get_tailsize(fd, fs.st_size, req_lines);
       119	
       120				if(!qflag)
       121					printf("<== %s ==>\n",fname);
       122	
       123				out_taillines(fd, size);
       124	
       125				close(fd);
       126			}else{
       127				perror("ERROR");
       128			}
       129	
       130		}
       131	
       132		return 0;
       133	}
    takk@deb9:~/tmp$ 
    

    一応ファイル末尾抽出する処理はできていると思うので、完成版となりますが、全133行とコンパクトにできてます。

    使ってみます。末尾3行抽出。

    takk@deb9:~/tmp$ gcc t.c
    takk@deb9:~/tmp$ ./a.out -3 a
    8
    9
    10
    

    オプション間違いの場合。

    takk@deb9:~/tmp$ ./a.out -3 a b
    ./a.out: invalid option -- '3'
    parameter error
    takk@deb9:~/tmp$ 
    

    複数ファイルを抽出。

    takk@deb9:~/tmp$ ./a.out -n3 a b
    <== a ==>
    8
    9
    10
    <== b ==>
    28
    29
    30
    takk@deb9:~/tmp$ 
    

    Byte単位で抽出。

    takk@deb9:~/tmp$ ./a.out -qc3 a b | od -tx1
    0000000 31 30 0a 33 30 0a
    0000006
    takk@deb9:~/tmp$ 
    

    動作も今のところ問題なさそう。

    本物tailのソースを読んで、答え合わせをしたいところですが、そろそろtailにも飽きてきたので、一旦終了。
    アニメでも見ながら、次の学習課題を探そうかと思います。

    コメント

    タイトルとURLをコピーしました