linux beginner - assign first file in directory to variable

i'm not a programmer but have managed to put some things together to use
for my artwork needs. i'm now trying to translate an xp powershell script
into c++ to run on my mandriva 2007.1 linux boxes but am completely stumped.

in powershell i can sort and return the first file in a directory in a
single line. how do i do this in c++?

i need to do this to process a folder full of timelapse still photos.
because they are large and there are often thousands of them, i find it
much faster to find the first file's name and increment through the
folder than to keep re-sorting and re-finding the next file. the files
all have the same name and are also numbered which makes incrementing
easy.

i've looked at readdir, which doesn't seem to sort, and scandir, which i
was somewhat able to get working. i couldn't get it to stop distinguishing
between upper and lowercase, though, so it produced two listings. have
had a lot of trouble with assigning the result to a variable i could use
elsewhere in the script.

if anyone has any tips, or even better, code, to do this it would be
much appreciated. been trying to do this one thing for a week and am
becoming suicidal.

thanks,
BabaG
scandir() is what you need to use. Try something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <sys/types.h>
#include <limits.h>
#include <dirent.h>
#include <stdlib.h>
#include <ctype.h>
#include <iostream>

using namespace std;

int my_select(const   struct   dirent   *dir)
{
   // return 0 to exclude this file or 1 to include this file
   if (strcmp(".", dir->d_name) == 0 ||
       strcmp("..", dir->d_name) == 0)
   {
      return 0;
   }
   else
   {
      return 1;
   }
}

int my_dcomp(const struct dirent  **first, const struct dirent **second)
{
   char file_1[PATH_MAX];
   char file_2[PATH_MAX];
   char *temp1 = file_1;
   char *temp2 = (char *)(*first)->d_name;
   
   // convert to upper case for comparison
   while (*temp2 != '\0')
   {
      *temp1++ = toupper((int)*(temp2++));
   }
   
   temp1 = file_2;
   temp2 = (char *)(*second)->d_name;
   
   while (*temp2 != '\0')
   {
      *temp1++ = toupper((int)*(temp2++));
   }
   
   return strcmp(file_1, file_2);
}
void my_func(char *directory_name)
{
   struct  dirent  **entries_p;
   char first_file[PATH_MAX];
   int stat = scandir(directory_name, &entries_p, my_select, my_dcomp);

   // -1 on failure or number of entries in the array
   if (stat > 0)
   {
      // This prints out the first file in the list to the console
      cout << "First file is " << entries_p[0]->d_name << endl;
   }

   // Free the memory allocated by scandir
   free(entries_p);
}

int main(int argc, char **argv)
{
    my_func("/home/usr");
}


It prints the first file to the console its running in. I've used char arrays rather that std::string because it fits in with the API calls more easily. The code needs cleaning up, it was put together in a rush some time ago, but it works.

Hope this helps

Bertha

thanks bertha.

your code above produced this error when i tried to compile it:
1
2
3
dir_cppdotcom-01.cpp: In function ‘void my_func(char*)’:
dir_cppdotcom-01.cpp:51: error: invalid conversion from ‘int (*)(const dirent**, const dirent**)’ to ‘int (*)(const void*, const void*)’
dir_cppdotcom-01.cpp:51: error:   initializing argument 4 of ‘int scandir(const char*, dirent***, int (*)(const dirent*), int (*)(const void*, const void*))’

while it seems considerably less robost than your code, i have
gotten this to do what i've needed:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <algorithm>					//added from computing.net tip
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
#include <vector>
#include <string>
#include <iostream>

using namespace std;

/*function... might want it in some class?*/
int getdir (string dir, vector<string> &files)
{

    DIR *dp;
    struct dirent *dirp;

    if((dp  = opendir(dir.c_str())) == NULL) {
        cout << "Error(" << errno << ") opening " << dir << endl;
        return errno;
    }

    while ((dirp = readdir(dp)) != NULL) {
        files.push_back(string(dirp->d_name));
    }

    closedir(dp);
    sort (files.begin(), files.end());			//added from computing.net tip

    return 0;
}

int main()
{

    string firstfile = "";

//    string dir = string(".");				//this directory
    string dir = string("/home/babag/Documents/Scripts/receiving");
    vector<string> files = vector<string>();

    getdir(dir,files);

    firstfile = files[2];				//print first file after (.) and (..)
    cout << firstfile << endl;

/*this for loop prints directory contents*/
//    for (unsigned int i = 2;i < files.size();i++) {	//i=0 includes . and ..; i=1 includes ..; i=2 starts on first file
//        cout << files[i] << endl;			//print each file in directory
//	  cout << files[0] << endl;			//print first file in directory (.) or 'this directory' symbol
//    }


    return 0;
}

i'd be curious for comments.

one thing i notice is that you have a routine for excluding . and ..
in the return. not sure how i'd incorporate that.

also, the code i have has another limitation in that it distinguishes
between upper and lowercase filenames. that, in effect, makes for
two separate sets of returns, one uppercase, the other lower.

does your code put all files into a single sort? or are upper and
lowercase sorted separately?

thanks very much for the help,
BabaG
First of all, the error my code generated is because your operating system has a slightly different prototype for scandir to the one I was working on (Sun Solaris).

To fix it change to parameters of my_dcomp...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
int my_dcomp(const void *f, const void *s)
{
   char file_1[PATH_MAX];
   char file_2[PATH_MAX];
   struct dirent  **first = (struct dirent  **)f;
   struct dirent **second = (struct dirent **)s;
   char *temp1 = file_1;
   char *temp2 = (char *)(*first)->d_name;
   
   // convert to upper case for comparison
   while (*temp2 != '\0')
   {
      *temp1++ = toupper((int)*(temp2++));
   }
   
   temp1 = file_2;
   temp2 = (char *)(*second)->d_name;
   
   while (*temp2 != '\0')
   {
      *temp1++ = toupper((int)*(temp2++));
   }
   
   return strcmp(file_1, file_2);
}


In your code to exclude . and .. I think the best place would be a conditional statement in
1
2
3
   while ((dirp = readdir(dp)) != NULL) {
        files.push_back(string(dirp->d_name));
    }

only do the files.push_back() if it is not . or ..

My code merges upper and lower case and yours could do it. The sort algorithm can take an extra parameter of a specific sort function object (see http://www.cplusplus.com/reference/algorithm/sort.html)
This function could sort so that it doesn't differentiate between upper and lower case. I do this is my code with my_dcomp(). Have a look at my code and the reference to the sort algorithm and see if you can come up with something.
Topic archived. No new replies allowed.