ソフトウェア工学入門 第 6 回コマンド作成 2 ファイルシステム
今後のスケジュール 5/28. コマンド作成 2 ( 本日 ) 6/4. ファイルシステム プロセス ハードウェア 6/11. 第 2 回個別試験
grep プログラム 1 次のプログラムを作成し grep.c という名前で保存しなさい #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <regex.h> static void do_grep(regex_t *pat, FILE *f); int main(int argc, char *argv[]) { regex_t pat; int err; int i; if (argc < 2) { fputs("no pattern\n", stderr); exit(1); err = regcomp(&pat, argv[1], REG_EXTENDED REG_NOSUB REG_NEWLINE); if (err!= 0) { char buf[1024]; regerror(err, &pat, buf, sizeof buf);
grep プログラム 2 つづき puts(buf); exit(1); if (argc == 2) { do_grep(&pat, stdin); else { for (i = 2; i < argc; i++) { FILE *f; f = fopen(argv[i], "r"); if (!f) { perror(argv[i]); exit(1); do_grep(&pat, f); fclose(f);
grep プログラム 3 regfree(&pat); exit(0); static void do_grep(regex_t *pat, FILE *src) { char buf[4096]; while (fgets(buf, sizeof buf, src)) { if (regexec(pat, buf, 0, NULL, 0) == 0) { fputs(buf, stdout); 練習ビルドして実行してみましょう >./grep opt head3.c int opt; while ((opt = getopt(argc, argv, "n:"))!= -1) { switch (opt) { nlines = atoi(optarg); if (optind == argc) { for (i = optind; i < argc; i++) {
grep の機能 grep : ファイル内を検索し 固定文字列を含む行を表示する grep で検索できる文字列は正規表現を含む 正規表現 記号 意味. 任意の 1 文字 * 直前のパターンの 0 文字以上の繰り返し? 直前のパターンは省略可能 特殊な意味を持つ文字を除外する 正規表現の例 a.*t a で始まって t で終わる文字列 Books? Book か Books (? の直前が s なので ) \* * を検索する
正規表現を処理する API #include <sys/types.h> #include <regex.h> int regcomp (regex_t *reg, const char *pattern, int flags ) ; void regfree (regex_t *reg ) ; int regexec ( const regex_t *reg, const char *string, size_t nmatch, regmatch_t pmatch[ ], int flags ) ; size_t regerror ( int errcode, const regex_t *reg, char *msgbuf, size_t msgbuf_size ) ; regcomp : 文字列で表現されている正規表現 pattern を reg に書き込む regfree : regcomp で確保した regex_t 領域を解放する regexec : regex_t を使って文字列を照合し reg が文字列 string に適合すれば 0 を返し適合しなければ 定数 REG_NOMATCH を返す regerror : 正規表現 API のエラーコードをエラーメッセージに変換する
grep 分析 1 do_grep static void do_grep(regex_t *pat, FILE *src) { char buf[4096]; while (fgets(buf, sizeof buf, src)) { if (regexec(pat, buf, 0, NULL, 0) == 0) { fputs(buf, stdout); 文字列をチェックするため 1 行単位で読み込まなければならない故に getc() ではなく fgets() を使用して 1 行単位で読み込んでいるストリーム src より 1 行づつ読み込み pat と適合する行 buf を出力する
grep 分析 2 main int main(int argc, char *argv[]) { regex_t pat; int err; int i; if (argc < 2) { fputs("no pattern\n", stderr); exit(1); err = regcomp(&pat, argv[1], REG_EXTENDED REG_NOSUB REG_NEWLINE); if (err!= 0) { char buf[1024]; regerror(err, &pat, buf, sizeof buf); puts(buf); exit(1); if (argc == 2) { do_grep(&pat, stdin); else { for (i = 2; i < argc; i++) { FILE *f; f = fopen(argv[i], "r"); if (!f) { perror(argv[i]); exit(1); do_grep(&pat, f); fclose(f); regfree(&pat); exit(0); コマンドライン引数を正規表現とみなして regex_t に変換し regerror でエラーチェック表示
せっかくなので 文字コードを学習 日本で利用されている主要文字コード EUC: UNIX で主流の文字コード Shift JIS: WINDOWS や MAC OS9 以前の OS などで主流の文字コード ISO-2022-JP: インターネットメールで使用される文字コード UTF-8(Unicode): 既存の文字コードを包括した最新の文字コード UTF-16(Unicode): JAVA で使用される文字コード 文字コードの仕組み 文字コード 符号化文字集合 エンコーディング
文字コード 符号化文字集合 : 文字コードで表現可能な範囲 JIS X : EUC Shift JIS ISO-2022-JP で使用される符号化文字集合 UCS : Unicode で使用される符号化文字集合 エンコーディング : 符号化文字集合に含まれる文字に割り当てられた特定数字を実際のバイト列に変換する計算式 ワイドキャラクタ : 全ての文字に対して同じバイト数を使用するエンコーディングマルチバイトキャラクタ : 文字の種類によってバイト数を変えるエンコーディング
文字化けとは? 文字コード エンコーディング 符号化文字集合採用規格 ISO-2022-JP マルチバイト JIS X メール EUC マルチバイト JIS X UNIX Shift JIS マルチバイト JIS X WINDOWS, MAC OS 9 UTF-8 マルチバイト UCS XML, Perl, MAC OS 10 UTF-16 ワイドキャラクタ UCS JAVA Unicode は特殊なため Unicode を含むと文字化けするケースが増える
ディレクトリ構造 UNIX ファイルシステムはルートディレクトリを頂点とする階層構造 ルート直下の重要ディレクトリ /etc : 各マシンごとの設定ファイルが置かれている /dev : ハードウェアと接続するためのデバイスファイルが置かれている /proc : プロセスファイルシステムがマウントされている /boot : カーネルのファイルが置かれている /root : スーパーユーザ専用のホームディレクトリ /home : 一般ユーザのホームディレクトリ /var : 頻繁に書き換えられるファイル専用のディレクトリ ( メール情報など ) /usr : 複数のユーザで共有可能なファイルが置かれている
プロセスファイルシステム プロセスファイルシステム : ファイルシステム上にプロセス情報を表現する仕組み 練習 プロセスファイルシステムを見てみましょう >ls /proc ここにプロセスファイルが表示されます 練習 ファイルの負荷情報を確認してみましょう >cat /proc/loadavg ここに使用しているシステムの負荷情報が表示されます
ディレクトリエントリー read ストリーム 構造体構造体構造体構造体構造体構造体 ディレクトリに記録されている情報はファイル情報の構造体 ディレクトリとはファイル情報を記録した構造体列 この構造体のことをディレクトリエントリーと呼ぶ
ディレクトリ操作のシステムコール ディレクトリを開く #include <sys/types.h> #include <dirent.h> DIR *opendir ( const char *path ) ; Path にあるディレクトリを読み込み ポインタ DIR に構造体を返す ディレクトリの読み込み #include <sys/types.h> #include <dirent.h> struct dirent *readdir ( DIR *d ) ; ディレクトリストリーム d からエントリを 1 つ読み込み エントリを返すディレクトリを閉じる #include <sys/types.h> #include <dirent.h> int closedir ( DIR *d ) ; ディレクトリストリーム d を閉じ 成功すれば 0 失敗すればー 1 を返す
ls コマンド作成 1 次のプログラムを作成し ls.c という名前で保存しなさい #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <dirent.h> static void do_ls(char *path); int main(int argc, char *argv[]) { int i; if (argc < 2) { fprintf(stderr, "%s: no arguments\n", argv[0]); exit(1); for (i = 1; i < argc; i++) { do_ls(argv[i]); exit(0); static void do_ls(char *path) { DIR *d; struct dirent *ent; d = opendir(path);
ls コマンド作成 2 次のプログラムを作成し ls.c という名前で保存しなさい if (!d) { perror(path); exit(1); while (ent = readdir(d)) { printf("%s\n", ent->d_name); closedir(d); 練習 ls を実行しましょう >gcc -o ls ls.c >./ls. ディレクトリの中身が表示されます
ls コマンド分析 static void do_ls(char *path) { DIR *d; struct dirent *ent; d = opendir(path); if (!d) { perror(path); exit(1); while (ent = readdir(d)) { printf("%s\n", ent->d_name); closedir(d); パス path にあるディレクトリを opendir() で開く path が存在しなかったり ディレクトリでなかったりすると NULL を返す ディレクトリエントリーがなくなるまで readdir() を使ってエントリを読み込む読み込んだエントリの名前を出力する
ディレクトリ作成 ディレクトリパスの作成 #include <sys/stat.h> #include <sys/types.h> int mkdir ( const char *path, mode_t mode ) ; ディレクトリ path を作成する 成功すれば 0 失敗したらー 1 を返し errno をセットする 第二引数はパーミッション設定 パーミッション設定 パーミッションは第二引数 mode のビットから umask に含まれるビットを落とす umask はプロセス属性 ( デフォルトは 022 がセットされている ) 例 mask = 777 1 1 1 1 1 1 1 1 1 umask= 022 0 0 0 0 1 0 0 1 0 ----------------------------------------------- 結果 = 755 1 1 1 1 0 1 1 0 1
mkdir コマンド作成 次のプログラムを作成し mkdir.c という名前で保存しなさい #include <stdio.h> #include <stdlib.h> #include <sys/stat.h> #include <sys/types.h> int main(int argc, char *argv[]) { int i; if (argc < 2) { fprintf(stderr, "%s: no arguments\n", argv[0]); exit(1); for (i = 1; i < argc; i++) { if (mkdir(argv[i], 0777) < 0) { perror(argv[i]); exit(1); exit(0);
mkdir 実行と rmdir 練習次のプログラムを実行しましょう >gcc -o mkdir mkdir.c >./mkdir dir >ls dir ディレクトリを削除する #include <unistd.h> int rmdir ( const char *path ) ディレクトリ path を削除する 成功すれば 0 失敗すればー 1 を返す 削除するディレクトリは必ず空でなければならない
rmdir コマンド作成 次のプログラムを作成し rmdir.c という名前で保存しなさい #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(int argc, char *argv[]) { int i; if (argc < 2) { fprintf(stderr, "%s: no arguments\n", argv[0]); exit(1); for (i = 1; i < argc; i++) { if (rmdir(argv[i]) < 0) { perror(argv[i]); exit(1); exit(0);
rmdir 実行 練習次のプログラムを実行しましょう >gcc -o rmdir rmdir.c >ls dir >./rmdir dir >ls なくなっていれば成功
本日の課題 コマンドライン引数からファイルを読み込み その行数を出力するコマンドを書きなさい 実行例 cat2.c の行数を確認してみる >./a.out cat2.c 24 > このようなプログラムを作成してください