#概要
簡単なcatコマンドを自作しました。オプションなしです。
コマンドライン引数からファイル名を受け取り、ファイルの中身を標準出力へ出力します。
低水準入出力(open,read,write,close)を使用しました。
ただし、低水準入出力は使い方によってはエラーが起こりやすいので、
特別な理由がない限りは標準ライブラリ(fopen,fread,fwrite,fclose)を使うことをお勧めします。
#ソースコード
#include<stdio.h> /* fprintf,perror */
#include<stdlib.h> /* exit */
#include<unistd.h> /* read,write,close */
#include<sys/types.h> /* read */
#include<fcntl.h> /* open */
#include<sys/uio.h> /* read */
static void simple_cat(const char *path);
static void die(const char *str);
int main(int argc, char *argv[])
{
int i;
if(argc < 2){
fprintf(stderr, "%s: file name not given\n",argv[0]);
exit(1);
}
for(i = 1;i < argc; i++)
simple_cat(argv[i]);
exit(0);
}
#define BUFSIZE 4096
static void simple_cat(const char *path)
{
int fd;
unsigned char buf[BUFSIZE];
ssize_t cc;
if((fd = open(path,O_RDONLY)) == -1)
die(path);
for(;;){
cc = read(fd, buf, sizeof buf);
if(cc == -1)
die(path);
if(cc == 0)
break;
if(write(STDOUT_FILENO, buf, cc) == -1)
die(path);
}
if(close(fd) == -1)
die(path);
}
static void die(const char *str)
{
perror(str);
exit(1);
}
#実行
$ gcc simple_cat.c
$ ./a.out (ファイル1) (ファイル1)
$ (ファイル1の内容)
(ファイル2の内容)
#解説
main()
int main(int argc, char *argv[])
{
int i;
if(argc < 2){
fprintf(stderr, "%s: file name not given\n",argv[0]);
exit(1);
}
for(i = 1;i < argc; i++)
simple_cat(argv[i]);
exit(0);
}
最初のif文でコマンドライン引数あるか確認して、なければエラーメッセージを出力してexit()します。
ここで出てきたargv[0]にはこのプログラムの名前が入っています。
次にfor文で全てのコマンドライン引数を順番に繰り返しsimple_cat()にファイル名を渡します。
simple_cat()
#define BUFSIZE 4096
static void simple_cat(const char *path)
{
int fd;
unsigned char buf[BUFSIZE];
ssize_t cc;
if((fd = open(path,O_RDONLY)) == -1)
die(path);
for(;;){
cc = read(fd, buf, sizeof buf);
if(cc == -1)
die(path);
if(cc == 0)
break;
if(write(STDOUT_FILENO, buf, cc) == -1)
die(path);
}
if(close(fd) == -1)
die(path);
}
最初にopen()でファイルを開きます。O_RDONLYは読み込み専用のオプションです。
戻り値はファイル記述子(FileDescriptor)という小さな非負整数です。
失敗した時は、-1が返されdie()を呼び出し処理を終了します。
次に無限ループのfor文の中でファイルの読み込み、書き込みを繰り返します。
read()はファイルにあるバイト列をプロセスのメモリ領域(バッファ)に読み込みます。
戻り値は実際に読み込んだバイト数です。
EOF(ファイルの終わり)に達すると0を返し、breakで無限ループを抜け出します。
失敗した時は、-1が返されdie()を呼び出し処理を終了します。
write()はバッファの内容をファイルに書き込みます。この場合は、関数の第一引数にSTDOUT_FILENOが設定されているので標準出力に書き込みます。
戻り値は実際に書き込んだバイト数です。
失敗した時は、-1が返されdie()を呼び出し処理を終了します。
最後にclose()でファイルを閉じます。
戻り値は成功すると0、失敗すると-1が返されます。
die()
static void die(const char *str)
{
perror(str);
exit(1);
}
perror()はエラーメッセージをエラー出力に出力します。
exit()はプロセスを終了します。
#参考文献
「ふつうのLinuxプログラミング第2版Linuxの仕組みから学べるgccプログラミングの王道」,青木峰郎 著,SBクリエイティブ株式会社,2017