ソフトウェア工学入門 第 5 回コマンド作成 1
head コマンド作成 1 早速ですが 次のプログラムを head.c という名前で作成してください #include <stdio.h> #include <stdlib.h> static void do_head(file *f, long nlines); int main(int argc, char *argv[]) { if (argc!= 2) { fprintf(stderr, "Usage: %s n\n", argv[0]); exit(1); do_head(stdin, atol(argv[1])); exit(0); 次のスライドへつづく
head コマンド作成 2 つづきです static void do_head(file *f, long nlines) { int c; if (nlines <= 0) return; while ((c = getc(f))!= EOF) { if (putchar(c) < 0) exit(1); if (c == '\n') { nlines--; if (nlines == 0) return;
head コマンドを実行してみる 練習 head.c をビルドして 実行してみましょう > gcc -o head head.c > cat args.c./head 4 #include<stdio.h> #include<stdlib.h> int main(int argc, char *argv [ ] ) head.c はファイルの先頭からコマンドライン引数の指定行までを出力するプログラム ただし このプログラムは標準入力のみです
head プログラム分析 1 main #include <stdio.h> #include <stdlib.h> static void do_head(file *f, long nlines); int main(int argc, char *argv[]) { if (argc!= 2) { fprintf(stderr, "Usage: %s n\n", argv[0]); exit(1); do_head(stdin, atol(argv[1])); exit(0); コマンドライン引数が指定されていなければ エラーを返すという処理 atoi 関数 ( 文字列を int 型の整数に変換 ) の親戚で atol 関数 ( 文字列を long 型の整数に変換 )
head プログラム分析 2 do_head static void do_head(file *f, long nlines) { int c; if (nlines <= 0) return; while ((c = getc(f))!= EOF) { if (putchar(c) < 0) exit(1); if (c == '\n') { nlines--; if (nlines == 0) return; 第一引数は読み込みストリーム 第二引数は表示する行数を表わしている 読み込みストリームから文字を読み込み \n が見付かると nlines を減らし nlines が 0 になったらプログラムを終了する ( つまり nlines は行数 )
不便じゃねぇ? このプログラムはちょっと不便ですよねぇ > gcc -o head head.c > cat args.c./head 4 #include<stdio.h> #include<stdlib.h> int main(int argc, char *argv [ ] ) cat でファイルを読み込み パイプでストリームを head に渡す これって不自然だし 不便です ファイルを直接読み込めるように改良しましょう
改良 head プログラム 1 次のプログラムを head2.c という名前で作成してください #include <stdio.h> #include <stdlib.h> static void do_head(file *f, long nlines); int main(int argc, char *argv[]) { long nlines; if (argc < 2) { fprintf(stderr, "Usage: %s n [file file...]\n", argv[0]); exit(1); nlines = atol(argv[1]); if (argc == 2) { do_head(stdin, nlines); else { int i; for (i = 2; i < argc; i++) { FILE *f; f = fopen(argv[i], "r"); if (!f) {
改良 head プログラム 2 つづきです exit(0); perror(argv[i]); exit(1); do_head(f, nlines); fclose(f); static void do_head(file *f, long nlines) { int c; if (nlines <= 0) return; while ((c = getc(f))!= EOF) { if (putchar(c) < 0) exit(1); if (c == '\n') { nlines--; if (nlines == 0) return;
改良部分をチェック nlines = atol(argv[1]); if (argc == 2) { do_head(stdin, nlines); else { int i; for (i = 2; i < argc; i++) { FILE *f; f = fopen(argv[i], "r"); if (!f) { perror(argv[i]); exit(1); do_head(f, nlines); fclose(f); exit(0); コマンドライン引数から行数を nlines に格納 読み込むファイルが 1 つの時の処理 読み込むファイルが 2 つ以上の時の処理
改良 head コマンドを実行してみる 練習 head2.c をビルドして 実行してみましょう > gcc -o head2 head2.c >./head2 4 args.c #include<stdio.h> #include<stdlib.h> int main(int argc, char *argv [ ] ) >./head2 4 args.c hello.c #include<stdio.h> int main ( int argc, char *argv[ ] ) ファイルを複数指定すれば コマンドライン引数で指定した行数分だけ表示される
もうちょっと本格的に! 一応 ファイルを読み込めていますが UNIX コマンドっぽくありません > gcc -o head2 head2.c >./head2 4 args.c #include<stdio.h> #include<stdlib.h> int main(int argc, char *argv [ ] ) >./head2 4 args.c hello.c #include<stdio.h> int main ( int argc, char *argv[ ] ) オプション指定で実行させてみる!
getopt() #include <unistd.h> int getopt (int args, char *const argv[ ], const char *optdecl ) ; extern char *optarg ; extern int optind, opterr, optopt ; getopt 関数の意味 UNIX 標準のオプション解析 API 基本的にはループ処理と一緒に使用され 返り値として発見したオプション値を与える第 1 引数と第 2 引数は main の引数をそのまま渡します第 3 引数は解析対象とするオプション全てを文字列として記載するオプションにパラメータがある場合には 次のグローバル変数で制御する 型 名前 意味 char* optarg 現在処理中のオプションのパラメータ int optind 現在処理中のオプションのargvのインデックス int optopt 現在処理中のオプション文字 int opterr 真ならばエラー時にエラーメッセージを表示する
オプション機能付き head 1 次のプログラムを head3.c という名前で作成してください #include <stdio.h> #include <stdlib.h> #include <unistd.h> static void do_head(file *f, long nlines); #define DEFAULT_N_LINES 10 int main(int argc, char *argv[]) { int opt; long nlines = DEFAULT_N_LINES; while ((opt = getopt(argc, argv, "n:"))!= -1) { switch (opt) { case 'n': nlines = atoi(optarg); break; case?': fprintf(stderr, "Usage: %s [-n LINES] [file...]\n", argv[0]); exit(1);
オプション機能付き head 2 つづきです if (optind == argc) { do_head(stdin, nlines); else { int i; for (i = optind; i < argc; i++) { FILE *f; f = fopen(argv[i], "r"); if (!f) { perror(argv[i]); exit(1); do_head(f, nlines); fclose(f); exit(0);
オプション機能付き head 3 更に つづきです static void do_head(file *f, long nlines) { int c; if (nlines <= 0) return; while ((c = getc(f))!= EOF) { if (putchar(c) < 0) exit(1); if (c == '\n') { nlines--; if (nlines == 0) return; 練習ビルドして 実行してみましょう >gcc -o head3 head3.c >./head3 -n 4 args.c 先頭から 4 行のプログラムが表示される
オプション機能付き head 分析 変更されている部分はここだけです int main(int argc, char *argv[]) { int opt; long nlines = DEFAULT_N_LINES; while ((opt = getopt(argc, argv, "n:"))!= -1) { switch (opt) { case 'n': nlines = atoi(optarg); break; case '?': fprintf(stderr, "Usage: %s [-n LINES] [file...]\n", argv[0]); exit(1); オプションの指定がなくなったところで while ループは終了する オプションが n ならば 行数指定のパラメータが読み込まれるオプションがないならば エラー表示される
デバッガの使い方 1 デバッガ : プログラムの間違った場所をチェックする仕組み C 言語のデバッガ : gdb gdbの下準備 gdbを使ってデバッグするにはビルドの段階で下準備が必要です 練習 gdb を使う準備をしましょう >gcc -Wall -g head3 head3.c > -g オプションを付けてビルドすることで gdb を使えるようになる
デバッガの使い方 2 練習 gdb を起動しましょう >gdb./head3 GNU gdb 5.2.1 Copyright 2002 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i386-vine-linux"... (gdb) ここにコマンド命令を書きます gdb の基本コマンド コマンド run backtrace list quit 動作 run 以下に命令を書くことで プログラムを実行する 不正な処理を行った場所がどこなのか表示する プログラムを表示する gdbを終了する