0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

簡単なcatコマンド自作(低水準入出力)

Last updated at Posted at 2021-01-30

#概要
簡単なcatコマンドを自作しました。オプションなしです。
コマンドライン引数からファイル名を受け取り、ファイルの中身を標準出力へ出力します。
低水準入出力(open,read,write,close)を使用しました。

ただし、低水準入出力は使い方によってはエラーが起こりやすいので、
特別な理由がない限りは標準ライブラリ(fopen,fread,fwrite,fclose)を使うことをお勧めします。

#ソースコード

simple_cat.c
#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

0
1
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?