気づけば(その9)まで来てしまいました。今回でtailっぽいことはできるようになりましたので、一応完成です。でも、まだまだ機能を追加していくので、タイトルを変えて続けると思います。
さて、本物版tailですが、特殊なオプション指定ができますよね。
tail -行数
このようにハイフンに続いて数字を指定する方法。まあ、普段から使ってるので、普通の方法と言えますが。
使ってみます。
takk@deb9:~/tmp$ seq 10 >a takk@deb9:~/tmp$ tail -3 a 8 9 10 takk@deb9:~/tmp$
普段使っていて気づかなかったのですが、使い方に制限があります。
作りを簡単にするような制限に思えます。-行数オプションの指定方法を間違えると、このようなエラーになります。
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にも飽きてきたので、一旦終了。
アニメでも見ながら、次の学習課題を探そうかと思います。
コメント