C言語関係掲示板

過去ログ

No.1010 ディレクトリのなかにあるディレクトリのすべてのファイル名をディレクトリ名と一緒に表示

[戻る] [ホームページ]
No.1157

ディレクトリのなかにあるディレクトリのすべてのファイル名をディレクトリ名と一緒に表示
投稿者---KAZ(2004/01/29 18:46:21)


LINUXのカレントディレクトリの中にあるディレクトリとその中ににある特定のファイル名をディレクトリ名と一緒に表示させようと思いプログラムをつくっているのですが、ディレクトリの中にあるディレクトリに移動する方法がわかりません。
例えば、このプログラムがいま、dir_1 というディレクトリにあるとして、その中にsample.txtとprac1.cとsample.htm、そして dir_2というディレクトリがあるとします。 dir_2の中にはsample.txt prac2.c があるとして、これらを以下のように表示させたいと思っています。

dir_1
prac1.c

dir_2
prac2.c

上のように拡張子が.cだけのものを撰び、ディレクトリ名と一緒に表示させたいのですが、方法がわかりません。以下に途中まで書いたプログラムを載せておきます。if 以下にディレクトリがあったら移動するようにしたいのですがどうかご教授ください。
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>


int main(int argc, char *argv[])
{
  char *dir;
  DIR *dp;
  struct dirent *entry;
 
  if(argc<2){
    dir = getenv("PWD");
  }
  else{
    dir = argv[1];
  }

  if((dp = opendir(dir))== NULL){
    perror("can not open");
    exit(1);
  }
  while((entry =readdir(dp)) != NULL){
    //ここから以下がつくれません。
    if(

    else if()
    printf("%s\n", entry->d_name);
  }

  closedir(dp);

  return 0;
}



No.1158

Re:ディレクトリのなかにあるディレクトリのすべてのファイル名をディレクトリ名と一緒に表示
投稿者---nop(2004/01/29 19:09:32)


再帰呼び出しで検索してみては?

No.1159

ディレクトリのなかにあるディレクトリのすべてのファイル名をディレクトリ名と一緒に表示
投稿者---KAZ(2004/01/29 20:46:14)


書き込みありがとうございます。ネットでさんざんさがしたのですが、すべてがディレクトリ内のファイルのパスを再帰的に表示するものばかりで、ディレクトリの中のディレクトリをOPENしてその中のファイル名を取得するものがなかなかみつかりません。自分でもさんざんトライしているのですが、うまくいきません。どうか具体的なアドバイスお願いします。

No.1165

再帰的にプログラムをかこうとしているのですが。。
投稿者---KAZ(2004/01/30 01:14:44)


どうしてもディレクトリの中のディレクトリにいくことができません。以下にプログラムを載せます。どうかわかるかたアドバイスをお願いします。

#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>


void dirlist(char *dir)
{
  DIR *dp;
  struct dirent *entry;
  struct stat statbuf;

 if(( dp = opendir(dir) ) == NULL ){
    perror("Can not open\n");
    exit(1);
  }
  
  while((entry = readdir(dp)) != NULL){
    stat(entry->d_name, &statbuf);
    if(S_ISDIR(statbuf.st_mode)){
      fprintf(stdout, "dir:%s\n", entry->d_name);
      dirlist(entry->d_name); // ここで再帰してるんですが。。
    }else{
      fprintf(stdout, "%s\n", entry->d_name);
    }
  }
  
  closedir(dp);
  


}

int main( int argc, char *argv[] )
{
  char *dir;
 
  
  if(argc<2){
    dir = getenv("PWD");
  }else{
    dir = argv[1];
  }

  dirlist(dir);
  
 
  return(0);
}




No.1167

Re:再帰的にプログラムをかこうとしているのですが。。
投稿者---YuO(2004/01/30 01:34:03)


UNIX系のシステムは持っていませんが……。

    if(S_ISDIR(statbuf.st_mode)){
      fprintf(stdout, "dir:%s\n", entry->d_name);
      dirlist(entry->d_name); // ここで再帰してるんですが。。


entry->d_nameは絶対パスではなく,
指定したディレクトリからの相対パスではないですか?

そうであれば,
char buffer[FILENAME_MAX];
sprintf(buffer, "%s/%s", dir, entry->d_name);
dirlist(buffer);

のようにしてやれば解決しませんか?

No.1168

Re:再帰的にプログラムをかこうとしているのですが。。
投稿者---かずま(2004/01/30 03:03:55)


#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>

void dirlist(char *dir)
{
    struct dirent *entry;  struct stat statbuf;  int len;

    DIR *dp = opendir(dir);
    if (dp == NULL) perror("opendir"), exit(1);

    len = strlen(dir);
    while ((entry = readdir(dp)) != NULL)
        if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")) {
            sprintf(dir + len, "/%s", entry->d_name);
            stat(dir, &statbuf);
            if (S_ISDIR(statbuf.st_mode)){
                printf("\ndir: %s\n", entry->d_name);
                dirlist(dir);
            } else {
                int n = strlen(entry->d_name);
                if (n > 2 && strcmp(entry->d_name + n - 2, ".c") == 0)
                    puts(entry->d_name);
            }
        }
    closedir(dp);
}

int main(int argc, char *argv[])
{
    char dir[FILENAME_MAX];

    strcpy(dir, argc < 2 ? "." : argv[1]);
    dirlist(dir);
    return 0;
}


No.1169

Re:再帰的にプログラムをかこうとしているのですが。。
投稿者---かずま(2004/01/30 03:15:02)


    printf("\ndir: %s\n", entry->d_name);
は
    printf("\ndir: %s\n", dir);

のほうがよいかもしれません。


No.1172

sprintfについて
投稿者---KAZ(2004/01/30 14:20:39)


すばやい解答ありがとうございます。昨日このプログラムに6時間つきあってたのですごくありがたいです。ただ、いくつもわからないことがたくさんあり、また質問させていただきます。

1:
プログラムのなかで最初にディレクトリ名であるdirの文字列の長さを得てそれをlenという変数にいれてるようなんですが、その後のsprintfでdir+len tというのがわかりません。dirは文字列で lenはintなのになぜ+???と思っているのですが、これは何をしているのでしょうか?

sprintf(dir + len, "/%s", entry->d_name);

2:
これも上の質問にちかいのですが、entry->d_nameはファイルの名前ですよね。ここでもういちど、intでnを宣言してファイルの名前の長さをとっているようなのですが、それから2をファイルの名前をたすというところがわかりません。
int n = strlen(entry->d_name);
if (n > 2 && strcmp(entry->d_name + n - 2, ".c") == 0)
puts(entry->d_name);

3:
statについてしらべてみたのですがいまいち理解できていません。

stat(dir, &statbuf);

ここで statbufはファイルのインフォメーションを得ているのでしょうか?
この後で、

S_ISDIR(statbuf.st_mode)

statbufがディレクトリかどうかを判定しているのですが、entry(direntの構造体はファイルかディレクトリかを判定できるものをもっていないのでしょうか?あとstatの構造体はst_mode意外にどのようなものをもっているのでしょうか?

4:
<pre>
>printf("\ndir: %s\n", entry->d_name);
>は
>printf("\ndir: %s\n", dir);

>のほうがよいかもしれません。
</pre>
とのことですが、なぜでしょうか?

どうかよろしくおねがいします。

No.1174

カレントディレクトリにあるプログラムが正しく表示されません。
投稿者---KAZ(2004/01/30 19:58:28)


何度もすいません。かずまさんに教えて頂いたプログラムなんですが、一番上のカレントディレクトリにあるファイルが一番したのディレクトリにあると表示されてしまいます。以下に私のディレクトリ構造と実行結果を書きます。

一番上、このプログラム(dir4.c)があるディレクトリ
./
dir4.c
sample1.c
sample2.c
sample3.c
ren2(ディレクトリ)

./ren2
samp2.c
ren3

./ren3
samp3.c

実行結果

sample1.c
sample2.c

dir: ren2
samp2.c

dir: ren3
samp3.c
dir4.c
sample3.c

このように一番最初のカレントディレクトリにあるsample3.c と dir4.c が一番最後に表示されてしまいます。sample2.c と sample1.c はただしく表示されているのになぜこのふたつだけがこのように表示されてしまうのでしょうか? 原因がまったくわかりません。どうか宜しくお願いします。

No.1177

Re:カレントディレクトリにあるプログラムが正しく表示されません。
投稿者---かずま(2004/01/31 13:24:37)


> このように一番最初のカレントディレクトリにあるsample3.c と dir4.c が
> 一番最後に表示されてしまいます。sample2.c と sample1.c はただしく表示
> されているのになぜこのふたつだけがこのように表示されてしまうのでしょうか?
 
カレントディレクトリにファイルとディレクトリを作るとき、
samplt1.c、sample2.c、ren2、dir4.c、sample3.c の順に作ったものと思われます。
readdir ではその順でファイル名を読み出しますから、このプログラムでは、ren2 の
下を表示した後、dir4.c、sample3.c を表示してしまいます。

この問題を解決するには、先にファイルを表示し、ディレクトリは後から表示
するようにすればよいでしょう。
 
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>

void dirlist(char *dir)
{
    struct dirent *entry;  struct stat statbuf;  char *tail;

    DIR *dp;
    
    printf("%s/\n", dir);
    dp = opendir(dir);
    if (dp == NULL) { perror("opendir"); return; }

    tail = dir + strlen(dir);
    *tail++ = '/';
    while ((entry = readdir(dp)) != NULL) {
        strcpy(tail, entry->d_name);
        stat(dir, &statbuf);
        if (!S_ISDIR(statbuf.st_mode)){
            int n = strlen(entry->d_name);
            if (n > 2 && strcmp(entry->d_name + n - 2, ".c") == 0)
                printf("  %s\n", entry->d_name);
        }
    }
    rewinddir(dp);
    while ((entry = readdir(dp)) != NULL) {
        if (strcmp(entry->d_name, ".") == 0) continue;
        if (strcmp(entry->d_name, "..") == 0) continue;
        strcpy(tail, entry->d_name);
        stat(dir, &statbuf);
        if (S_ISDIR(statbuf.st_mode)) dirlist(dir);
    }
    closedir(dp);
}

int main(int argc, char *argv[])
{
    char dir[FILENAME_MAX];

    strcpy(dir, argc < 2 ? "." : argv[1]);
    dirlist(dir);
    return 0;
}


No.1181

rewinddirについて
投稿者---KAZ(2004/01/31 15:43:22)


かずまさん、非情に丁寧な解説ほんとうに感謝します。勉強になります。あとひとつだけ聞かせてください。lengthをはかって文字列の最後をさすポインタをみつける方法すごく効率てきで感動しました。今まで、forでまわして、'\0'をさがしてたので。私はまだCをはじめたばかりの初心者で、このような方法など教えていただくとすごく勉強になります。

プログラム中に

rewinddir(dir);

とありますが、これはなんのためなのでしょうか?
しらべたところ、ディレクトリハンドルを元にもどすというようなことが説明されているんですが、理解できません。どうかご教授ください。

No.1200

理解できました。(rewinddirについて)
投稿者---KAZ(2004/02/01 19:46:52)


ようやく理解することができました。最初にディレクトリのなかのファイルをプリントして、その後にrewinddirをつかってもう一度ディレクトリを開きなおして、今度はディレクトリの名前だけをプリントし、再帰していたんですね。リカージョンのプログラムはいつも予測していなかったことがおこり、すごく苦手意識があるんですが、このように一発ですべてのディレクトリを検索するようなプログラムをみるとかっこよく思えます。もっとリカージョンのプログラムにもなれねばと感じました。これからこのプログラムをもっといじって勉強しようと思います。次はすべてのディレクトリのファイルのなかの特定の文字を検索できるようにしてみようかなと思ってます。いろいろと教えていただき本当にありがとうございました。すごく勉強になりました。システムライブラリっていろんなものが用意させてるんですね。

No.1176

Re:sprintfについて
投稿者---かずま(2004/01/31 13:20:42)


> 1:
> プログラムのなかで最初にディレクトリ名であるdirの文字列の長さを得て
> それをlenという変数にいれてるようなんですが、その後のsprintfでdir+len
> tというのがわかりません。dirは文字列で lenはintなのになぜ+???と思って
> いるのですが、これは何をしているのでしょうか?

main の dir は、char の配列です。
その中には、ディレクトリ名の文字列が入っています。
dirlist の dir は、char へのポインタです。
ポインタ dir は、main の dir[0] を指しています。
dir + len は、ディレクトリ名の文字列を終端する '\0' を指しています。


> 2:
> これも上の質問にちかいのですが、entry->d_nameはファイルの名前ですよね。
> ここでもういちど、intでnを宣言してファイルの名前の長さをとっているよう
> なのですが、それから2をファイルの名前をたすというところがわかりません。

entry->d_name は、ファイル名の文字列(の先頭)を指すポインタです。
entry->d_name + n は、ファイル名を終端する '\0' を指すポインタです。
entry->d_name + n - 2 は、ファイル名の最後から 2番目の文字を指すポインタです。


> 3:
> statについてしらべてみたのですがいまいち理解できていません。

何を調べたのですか?
Linus を使っているのだったら、man 2 stat を実行してみてください。


> 4:

ディレクトリの階層が深くなると違いがわかるはずですが。

No.1178

Re:sprintfについて
投稿者---かずま(2004/01/31 13:33:01)


> Linus を使っているのだったら、man 2 stat を実行してみてください。

Linus は Linux の間違いです。

Linux を作ったのは Linus Torvalds ですね。


No.1255

linuxの一番上の階層localhostでプログラムを実行すると。。
投稿者---KAZ(2004/02/05 18:00:40)


かずまさんに以前、教えて頂いた以下のプログラムをlinux上の自分のアカウントの最初の階層で実行すると/以下のすべてのディレクトリを読みにいくのですがこれはなぜでしょうか?またこれを回避することは可能なんでしょうか?

わたしのLINUXは次のようになってます。

/home/kaz/

このプログラムをkaz−ディレクトリで実行するとすべてのルート以下の階層を開いて行きます。
例)/usr/bin/


#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>

void dirlist(char *dir)
{
struct dirent *entry; struct stat statbuf; char *tail;

DIR *dp;

printf("%s/\n", dir);
dp = opendir(dir);
if (dp == NULL) { perror("opendir"); return; }

tail = dir + strlen(dir);
*tail++ = '/';
while ((entry = readdir(dp)) != NULL) {
strcpy(tail, entry->d_name);
stat(dir, &statbuf);
if (!S_ISDIR(statbuf.st_mode)){
int n = strlen(entry->d_name);
if (n > 2 && strcmp(entry->d_name + n - 2, ".c") == 0)
printf(" %s\n", entry->d_name);
}
}
rewinddir(dp);
while ((entry = readdir(dp)) != NULL) {
if (strcmp(entry->d_name, ".") == 0) continue;
if (strcmp(entry->d_name, "..") == 0) continue;
strcpy(tail, entry->d_name);
stat(dir, &statbuf);
if (S_ISDIR(statbuf.st_mode)) dirlist(dir);
}
closedir(dp);
}

int main(int argc, char *argv[])
{
char dir[FILENAME_MAX];

strcpy(dir, argc < 2 ? "." : argv[1]);
dirlist(dir);
return 0;
}