C言語で自作したcpコマンドが上手く動作しない

このQ&Aのポイント
  • C言語で自作したcpコマンドが上手く動作しない問題について解説します。
  • ファイルのパーミッションが「----------」となっているため、読み書き実行ができない状態です。
  • ソースコードの一部を修正することで、ファイルのコピーが正常に行われるようになります。
回答を見る
  • ベストアンサー

C言語で自作したcpコマンドが上手く動作しません

当方、プログラミングを勉強中の学生です。 先日、ファイル入出力関数を用いてcpコマンドを自作しました。 一応、コンパイルは通るのですが、コピーしたファイルを開くことができません。 そのファイルのパーミッションを確認してみたところ 「----------」となっており、読み書き実行すべて不可となっていました。 ソースは以下の通りなのですが、何が問題でしょうか。 回答よろしくお願い致します。 #include<stdio.h> #include<fcntl.h> #include <string.h> #include <errno.h> #include <sys/types.h> #include <sys/uio.h> #include <unistd.h> #define SIZE 8192 int main(int argc, char *argv[]) { int fd1, fd2; char buf[SIZE]; if ( argc != 3 ){ char err_message[] = "ファイル名を指定して下さい。\n"; write(2, err_message, strlen(err_message)); return 1; } argv[0] = "mycopy"; fd1 = open(argv[1], O_RDONLY); fd2 = open(argv[2], O_WRONLY | O_CREAT); if (fd1 < 0 || fd2 < 0) { char err_message[] = "ファイルをオープンできません。"; write(2, err_message, strlen(err_message)); write(2, strerror(errno), strlen(strerror(errno))); write(2, "\n", 1); return 1; } while(1) { if (read(fd1, buf, SIZE) == 0) { break; } else if (read(fd1, buf, SIZE) > 0) { write(fd2, buf, SIZE); } else { char err_message[] = "エラーが発生しました。"; write(2, err_message, strlen(err_message)); write(2, strerror(errno), strlen(strerror(errno))); write(2, "\n", 1); return 1; } } close(fd1); close(fd2); return 0; }

質問者が選んだベストアンサー

  • ベストアンサー
  • entree
  • ベストアンサー率55% (405/735)
回答No.4

> そのファイルのパーミッションを確認してみたところ > 「----------」となっており、読み書き実行すべて不可となっていました。 openは第三引数にパーミッションを指定できます。0666を設定すると期待した動作になるのではないでしょうか。(umaskが022なら、作成されるファイルのパーミッションは644になります) その他の点について。 1.fd1, fd2をopenしてからエラーチェックしていますが、fd1のオープンに失敗してfd2のオープンが成功すると、O_CREATがあるために空ファイルができてしまう。  ==> 面倒でもエラーチェックは2つに分けるべきでしょう。 2.fd2のopen時にO_TRUNCがないと、ファイルがあった場合の動作がおかしいことになる。  ==> fd1のファイルサイズ < fd2のファイルサイズのファイルが存在する状況を作って、やってみるとわかると思います。read側の内容がabc、write側の内容がhijklmnなら、実行後のwrite側の内容はabcklmnになってしまいますよね? 期待される動作はabcのはずです。 3.read, writeのエラーチェックでEINTR(割り込み)に対する考慮がされていない。  ==> EINTRの場合はエラーとはみなさずリトライする処理が必要です。 4.read成功時の読み取りバイト数がSIZEであるという仮定が置かれている。  ==> 実際にはそれよりも少ないこともあり得ます。 5.nun00nunさんのコードでは、write成功時の書き込みバイト数がSIZEであるという仮定が置かれている。  ==> 実際にはそれよりも少ないこともあり得ます。その場合は残りの部分の書き出し処理が必要です。 以下、私の方で修正したコードです。 #include <stdio.h> #include <fcntl.h> #include <string.h> #include <errno.h> #include <unistd.h> #define SIZE 8192 int main (int argc, char *argv[]) {  int fd1, fd2;  char buf[SIZE], *bufp;  int rsize, wsize;  if (argc != 3)   {    char err_message[] = "ファイル名を指定して下さい。\n";    write (2, err_message, strlen (err_message));    return 1;   }  argv[0] = "mycopy";  fd1 = open (argv[1], O_RDONLY);  if (fd1 < 0)   {    char err_message[] = "ファイルをオープンできません。";    write (2, err_message, strlen (err_message));    write (2, strerror (errno), strlen (strerror (errno)));    write (2, "\n", 1);    return 1;   }  fd2 = open (argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0666);  if (fd2 < 0)   {    char err_message[] = "ファイルをオープンできません。";    write (2, err_message, strlen (err_message));    write (2, strerror (errno), strlen (strerror (errno)));    write (2, "\n", 1);    return 1;   }  while (1)   {    bufp = buf;    rsize = read (fd1, bufp, SIZE);    if (rsize == -1)     {      if (errno == EINTR)       {        continue;       }      char err_message[] = "読み込みエラーが発生しました。";      write (2, err_message, strlen (err_message));      write (2, strerror (errno), strlen (strerror (errno)));      write (2, "\n", 1);      return 1;     }    if (rsize == 0)     {      break;     }    while (1)     {      wsize = write (fd2, bufp, rsize);      if (wsize == -1)       {        if (errno == EINTR)         {          continue;         }        char err_message[] = "書き込みエラーが発生しました。";        write (2, err_message, strlen (err_message));        write (2, strerror (errno), strlen (strerror (errno)));        write (2, "\n", 1);        return 1;       }      bufp += wsize;      rsize -= wsize;      if (rsize == 0)       {        break;       }     }   }  close (fd1);  close (fd2);  return 0; }

nun00nun
質問者

お礼

お礼が遅くなってしまい失礼しました。 訂正したコードまで付けてくださるとは(泣) 勉強させていただきます。 ありがとうございました!

その他の回答 (3)

  • trapezium
  • ベストアンサー率62% (276/442)
回答No.3

細いことだけど。 > fd1 = open(argv[1], O_RDONLY); > fd2 = open(argv[2], O_WRONLY | O_CREAT); > if (fd1 < 0 || fd2 < 0) { fd1 が失敗しても、fd2 が成功した段階で errno がクリアされるので、その下のエラーメッセージが正しくないときがある。 > write(2, strerror(errno), strlen(strerror(errno))); その都度処理するか、errno を保存しておく。 まあ、err(1, なんたらでいいじゃないかと思わないこともないが、stdio 使わないことに意味があるんだろうと write(2, に関しては…

nun00nun
質問者

お礼

お礼が遅くなってしまい失礼しました。 回答ありがとうございます。勉強になりました!

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.2

ついでにいうと, read の返り値を捨てちゃうのもまずかったような.

nun00nun
質問者

お礼

おっしゃるとおりですね。プログラムを修正させていただきました。 回答ありがとうございました!

  • f272
  • ベストアンサー率46% (7995/17089)
回答No.1

fd2 = open(argv[2], O_WRONLY | O_CREAT); の第3引数にパーミッションを指定してみたらどうかな? それよりも whileの中で if (read(fd1, buf, SIZE) == 0) { でfd1から読んで,また } else if (read(fd1, buf, SIZE) > 0) { でfd1から読むのは意図と違う気がする。

nun00nun
質問者

お礼

回答ありがとうございます。 ご指摘の通り、プログラム自体もよろしくありませんでした(^^:) 参考にさせていただきます!

関連するQ&A

  • mmapで自作cat

    はじめまして。C言語(というかプログラミング自体)まったくの初心者です。 mmapで自作catコマンドを作りたいのですが、 以下のソースコードでコンパイルできたものの、 a.outするとsegmentation faultとなってしまいます。 もしよろしければ何がいけないか、ご指摘いただけると嬉しいです。 1 #include<sys/types.h> 2 #include<sys/stat.h> 3 #include<sys/mman.h> 4 #include<fcntl.h> 5 #include<unistd.h> 6 #include<stdio.h> 7 #include<string.h> 8 9 int main (int argc, char*argv[]){ 10 11 int fd; 12 char *m; 13 int size; 14 15 if(argc < 3){ 16 open(argv[1],O_RDWR); 17 } 18 19 fd = open(argv[1], O_RDWR); 20 if(fd < 0){ 21 printf("error\n"); 22 }else{ 23 fseek(0, 0L, SEEK_END); 24 size = ftell(0); 25 } 26 27 m = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); 28 29 if(m == MAP_FAILED){ 30 printf("error\n"); 31 }else{ 32 write(1,m,strlen(m)); 33 34 close(fd); 35 return 0; 36 } 37 }

  • このプログラムの動作について教えてください

    #include<stdio.h> #include<unistd.h> #define SIZE 10 int main(int argc,char *argv[]){ int fd; char buf[SIZE]; fd=open("data",0); read(fd,buf,10) write(1,buf,10); close(fd); rerutn 0; } というプログラムで dataの中身が以下のテキストファイルとなっているようなのんですが dataの内容:abcdefghijklmnopqrstuvwxyz このプログラムの出力結果がabcabcabce となるとの事なのですが何故でしょうか? 普通に先頭から10バイト分読み込んで出力するならabcdefghijとなるのではないのでしょうか? どうぞご教授お願いします

  • c言語 iconv

    msys環境で実行して,指定されたファイルの文字コードをShift-JISに変換して表示するコードを作成しているのですがうまく表示されません.何がいけないのでしょうか.第1引数に変換対象のファイル名,第2引数にファイルの文字コードを指定しています. #include <stdio.h> #include <string.h> #include <iconv.h> int main(int argc, char *argv[]) { iconv_t conv; char src[10000]; char dst[10000]; int src_len = strlen(src); int dst_len = sizeof(dst) - 1; char *buf_in; char *buf_out; FILE *fp; fp = fopen(argv[1], "r"); if(NULL == fp) { printf("ファイルを開けません\n"); } else { while(fgets(src, 10000, fp) != NULL) { buf_in = src; buf_out = dst; /* 変換器を作成 */ conv = iconv_open("Shift-JIS", argv[2]); /* 変換 */ iconv(conv, &buf_in, &src_len, &buf_out, &dst_len); *buf_out = '\0'; /* 終末処理 */ /* 文字コード後の文字列を表示 */ printf("%s\n", dst); } /* 変換器を終了 */ iconv_close(conv); /*ファイルを閉じる*/ fclose(fp); } return 0; }

  • C言語ソケットでWikipediaの情報入手

    以下のCコードで > gcc wiki_client.c -o wiki_client > ./wiki_client "O'Reilly_Media" として https://en.wikipedia.org/wiki/O%27Reilly_Media の情報を得ようとしているのですが、 HTTP/1.1 400 Bad Request が返ってきます。 これは本に載っていたままのコードなのですが、 どこをどう直せばいいのか分かりません。 どこを直せばいいか教えて下さい。 ブラウザからはもちろんアクセスできます。 環境はUbuntu 18.04のgcc 7.3.0です。 ではよろしくお願いします。 // wiki_client.c #include <stdio.h> #include <string.h> #include <errno.h> #include <stdlib.h> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #include <netdb.h> void error(char *msg) { fprintf(stderr, "%s: %s\n", msg, strerror(errno)); exit(1); } int open_socket(char *host, char *port) { struct addrinfo *res; struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if (getaddrinfo(host, port, &hints, &res) == -1) error("Can't resolve the address"); int d_sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (d_sock == -1) error("Can't open socket"); int c = connect(d_sock, res->ai_addr, res->ai_addrlen); freeaddrinfo(res); if (c == -1) error("Can't connect to socket"); return d_sock; } int say(int socket, char *s) { int result = send(socket, s, strlen(s), 0); if (result == -1) fprintf(stderr, "%s: %s\n", "Error talking to the server", strerror(errno)); return result; } int main(int argc, char *argv[]) { int d_sock; d_sock = open_socket("en.wikipedia.org", "80"); char buf[255]; sprintf(buf, "GET /wiki/%s http/1.1\r\n", argv[1]); say(d_sock, buf); say(d_sock, "Host: en.wikipedia.org\r\n\r\n"); char rec[256]; int bytesRcvd = recv(d_sock, rec, 255, 0); while (bytesRcvd) { if (bytesRcvd == -1) error("Can't read from server"); rec[bytesRcvd] = '\0'; printf("%s", rec); bytesRcvd = recv(d_sock, rec, 255, 0); } close(d_sock); return 0; }

  • C言語 エラーチェックについて

    初めまして。分からないところがあったので質問させていただきます。 以下のプログラムは引数から値を取得し、その値で生データの切り出しを 行うプログラムです。 read関数のところなのですが、argv[1]に格納されたファイル(パス名)が0byteでもエラーが出力されずに、コンパイルされてしまいます。 どうすればよいのでしょうか? さらに、上司にreadとwriteにエラーチェックのメッセージをつけろ。と言われたのですが、どうやれば良いのかよく分かりませんでした。if()~とすれば良いのでしょうか?初心者なので分かりにくい質問かと思いますが、どうぞよろしくお願いします。 #pragma warning ( disable : 4996 ) #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <io.h> #include <sys/stat.h> #define ONESEGSIZE 2048 void main ( argc, argv ) int argc; char *argv[]; { int fd, fd2; // ファイルハンドル int segment; // 列数 int raw_n; // 切り出す生データの数 int tviews; // 総ビュー数 int start_view; // 切り出し開始ビュー int size_view; // 切り出しサイズ int pitch_view; // 切り出しピッチ char *Rawdivit; // コマンド char *raw_file; // 生データファイル名 char *outraw_file; // 生データ出力ファイル名 char *mem; // メモリ変数 /* パラメータの個数チェック */ if ( argc != 9 ) { printf( "usage : Rawdivit raw_file outraw_file segment raw_n" " tviews start_view size_view pitch_view \n" ); exit ( 1 ); } /* 引数の取得char型 */ Rawdivit = argv[0]; raw_file = argv[1]; outraw_file = argv[2]; /* 引数の取得int型 */ segment = atoi ( argv[3] ); raw_n = atoi ( argv[4] ); tviews = atoi ( argv[5] ); start_view = atoi ( argv[6] ); size_view = atoi ( argv[7] ); pitch_view = atoi ( argv[8] ); /* 生データファイルオープン */ fd = open ( raw_file, O_RDONLY | O_BINARY ); if ( fd == -1 ) { printf ( "Fileopen error : read\n" ); exit ( -1 ); } /* 生データ読み込み用メモリ確保 */ mem = ( char * ) malloc ( ONESEGSIZE * segment * size_view ); // 単位 = byte if ( mem == NULL ) { printf ( "Memorysecure error\n" ); exit ( -1 ); } /* 切り出し開始位置までファイルポインタをシーク */ lseek ( fd, ONESEGSIZE * segment * start_view, SEEK_SET ); /* 切り出しサイズ分読み込み */ read ( fd, mem, ONESEGSIZE * segment * size_view ); /* 出力ファイルオープン */ fd2 = open ( outraw_file, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IREAD | S_IWRITE ); if ( fd2 == -1 ) { printf ( "Fileopen error : write\n" ); exit ( -1 ); } /* 読み込んだデータを出力ファイルに書き込み */ write ( fd2, mem, ONESEGSIZE * segment * size_view ); /* メモリの開放 */ free ( mem ); /* ファイルクローズ */ close ( fd ); close ( fd2 ); exit ( 0 ); }

  • C言語におけるfgetsを用いたループ処理について

    C言語において、文章を読み込むためにfgetsを用いて下記のプログラムを書いたのですが、*において入力を受け付けなくなります。 これを解消する方法はないでしょうか? ちなみにコンパイラはBBC、開発環境はVistaです。 なお、簡略のため#include,main等は省略しています。 //以下プログラム #define SIZE 16384 char moji[SIZE] = {""}; char buf[SIZE]; //EOF(^Z)になるまで、文字列を受け取る while(fgets(buf, sizeof(buf), stdin) != NULL){ if(sizeof(moji) < strlen(moji) + strlen(buf)) break; strcat(moji, buf); }; getchar(); //* //プログラムここまで

  • 質問させてください(かなり長いです)

    int main(int argc,char *argv[]) { if(argc != 3){ char err_message[] = "ファイル名を指定して下さい、またはファイル名が多すぎる\n"; write(2,err_message, strlen(err_message)); exit(EXIT_FAILURE); } char *InFile = argv[1]; /*入力ファイル名設定*/ char *OutFile = argv[2]; /*出力ファイル名設定*/ myclass ob(InFile,OutFile); ob.open(); ob.main(); ob.close(); return(0); } このようなソースがありif(arg=!3)のところを実行して終了してしまいます。よくint main(int argc,char *argv[])という書き出しを見ますがmainの引数はどこで使われているのでしょうか。よろしければご教授ください。

  • 次の問題の解答をお願いします。

    次の問題の解答をお願いします。 次のプログラムには問題があり、コピー先ファイルへ書き込んでいる途中にエラーが発生した場合を想定していない。エラーが発生した場合にメッセージを表示してすぐに終了するようにプログラムを修正せよ。また各行の説明もせよ。 #include<stdio.h> #include<stdlib.h> #include<errno.h> #include<string.h> int main(int argc, char*argv[]) { FILE*source_fp,*dest_fp; int ch; if(argc!=3){ fprintf(stderr,"使い方: %s original.txt copy.txt\n",argv[0]); exit(EXIT_FAILURE); } if((source_fp=fopen(argv[1],"rb"))==NULL){ fprintf(stderr,"%s failed: %s\n", argv[1],strerror(errno)); exit(EXIT_FAILURE); } if((dest_fp_fp=fopen(argv[2],"wb"))==NULL){ fprintf(stderr,"%s failed: %s\n",argv[2],strerror(errno)); exit(EXIT_FAILURE); } while ((ch=getc(source_fp))!=EOF){ putc(ch,dest_fp); } fclose(source_fp); fclose(dest_fp); return 0; エラーメッセージを表示するにはどうしたらいいのかよくわかりません。 よろしくお願いします。

  • C言語 Segmentation fault

    program SPROGRAM 17 4 pas104 SIDENTIFIER 43 4 ( SLPAREN 33 4 上記のようなファイルを読みこんで、1行づつ各トークン(programとか17とか)を構造体に格納する関数reader()を作りましたが、これを以下のparser.cで呼びだすと「Segmentation fault(core dumped)」となってしまいます。gdbのrunコマンドでプログラムを実行すると、関数を呼び出す所で Program received signal SIGSEGV, Segmentation fault. 0x0040140f in reader () と表示されました。が,どうすればよいか全然分かりません・・・ どうすれば正しく動作してくれるのか、どなたか教えてください・・・ 以下ソース /*read.c*/ #include "head.h" void reader(void) { fprintf(stdout,"check"); char buf[BUF_LEN]; if (fgets(buf,sizeof(buf),fp) == NULL)/*ここで1行読みこむ*/ {/*いきなりNULLの場合*/ strcpy(t.str, ""); t.num = SEOF; t.line = 1; } else { 省略 } return; } /*parser.c*/ #include "head.h" struct tokenbox t; FILE *fp; int main(int argc, char *argv[]) { if (argc != 2)/*引数チェック*/ { fprintf(stderr,"Usage: (./parser) (file.ts)\n"); return -1; } int len; len = strlen(argv[1]);/*file.ts の長さ取得*/ if((argv[1][len-1] == 's') && (argv[1][len-2] =='t') && (argv[1][len-3] == '.'))/*tsファイルが指定されているかどうか*/ { fp = fopen(argv[1],"r");/*ファイルオープン*/ if (fp == NULL) { fprintf(stderr,"Such ts file is not exist\n"); return -1; } fprintf(stdout,"authenticate ts file!\n");//←これは出力される reader(); //←ここでSegmentation faultと思われる printf("t.str = %s\n", t.str); printf("t.str[0] = %c\n", t.str[0]); printf("t.num = %d\n", t.num); printf("t.line = %d\n", t.line); fclose(fp); return 0; } else { fprintf(stderr,"the file is not ts\n"); return -1; } } ヘッダファイル一部抜粋 /*head.h*/ #include <stdio.h> #include <string.h> #include <stdbool.h> #define BUF_LEN 128 #define TOKEN_LEN 128 struct tokenbox {/*tsファイルの各情報を格納する構造体*/ char str[TOKEN_LEN]; int num; int line; }; extern struct tokenbox t;/*構造体をtと置く*/ extern FILE *fp; /*ファイルポインタ*/

  • C言語に関する質問

    初めて質問させて頂きます。C言語初心者です。 実は講義で「ファイル中の英文を単語に分けてその出現頻度をカウントするコードを木構造を用いて出力せよ」という課題が出ました。 そこで、参考にするコードを検索しましたところ、以下のURLにあるベストアンサーのコードが近いと感じました。 http://okwave.jp/qa/q4155655.html コードの内容は以下の通りになります。 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> typedef struct node Node; struct node{ char *word; int count; Node *left,*right; }; Node *root=NULL; void compose(FILE *fp); void inorder(Node *p); void strlower(char *s); int main(int argc, char *argv[]) { FILE *fp; Node *new; fp=fopen(argv[1],"r"); if(fp==NULL){puts("ファイルを開けません");return(-1);} compose(fp); inorder(root); return (0); } void strlower(char *s){ while(*s!=NULL){*s=tolower(*s);s++;} } void compose(FILE*fp){ Node **p,*new; char buf[256]; while(1){ fscanf(fp,"%[^a-zA-Z0-9]",buf); if(fscanf(fp,"%[a-zA-Z0-9]",buf)==EOF)break; strlower(buf); if(root==NULL){ new=(Node *)malloc(sizeof(Node)); new->left=NULL; new->right=NULL; new->word=strdup(buf); new->count=1; root=new; }else{ *p=root; while(1){ if(strcmp(buf,(*p)->word)==0){ (*p)->count++;break; }else if(strcmp(buf,(*p)->word)<0){ if((*p)->left==NULL){ new=(Node *)malloc(sizeof(Node)); new->left=NULL;new->right=NULL;new->word=strdup(buf);new->count=1; (*p)->left=new; break; }else{ *p=(*p)->left; } }else{ if((*p)->right==NULL){ new=(Node *)malloc(sizeof(Node)); new->left=NULL; new->right=NULL; new->word=strdup(buf); new->count=1; (*p)->right=new; break; }else{ *p=(*p)->right; } } } } } } void inorder(Node*p){ if (p==NULL) return; inorder(p->left); printf("%s %d\n",p->word, p->count); inorder(p->right); } しかし、これをそのままコンパイル・実行すると、コンパイル時に以下の注意が出ます。 warning comparison between pointer and integer ('int' and 'char *') while(*s!=NULL){*s=tolower(*s);s++;} 上記の注意を無視してそのまま実行すると、segmatation faultが出てしまいますorz おそらく、sの型が*s=s[]なので、注意の中の「s++」の部分で誤作動を起こしている(s++を実行するにはsはint型でなければならない)と思うのですが、どうコード文を変えれば良いのかがよくわかりません。 どなたかお教え頂けると幸いです。どうぞよろしくお願いしますm(_ _)m

専門家に質問してみよう