Blog der Heimetli Software AG

Linux: sortiertes Directory-Listing mit scandir

Heute stand ich vor der Aufgabe, Files mit einer bestimmten Endung in einem Directory zusammenzusuchen und zu sortieren.

An sich ist das kein grosses Problem, mit opendir das Directory öffnen, mit readdir die einzelnen Einträge lesen, die passenden Filenamen speichern und sortieren, und am Ende mit closedir wieder aufräumen.

Beim Grübeln nach den Details zu den einzelnen Calls bin ich aber auf die mir bisher unbekannte Funktion scandir gestossen.

Die liest das Directory aus, prüft mit Hilfe einer Filterfunktion ob der Eintrag gespeichert werden soll, und sortiert die gefundenen Files mit Quicksort. Also genau das was ich brauchte !

Und nicht nur das wird einem abgenommen, die Vergleichsfunktion für die alphabetische Sortierung wird gleich mitgeliefert.

Ein Testprogramm für scandir fällt entsprechend kurz aus:

/******************************************************************************/
/*                                                                            */
/*                                                              FILE: dir.cpp */
/*                                                                            */
/*    Simple demo program for scandir                                         */
/*    ===============================                                         */
/*                                                                            */
/*    Prints a sorted list of all "*.txt" files in a directory                */
/*                                                                            */
/*    V1.00  23-MAY-2013   P. Tellenbach, http://www.heimetli.ch              */
/*                                                                            */
/******************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>

/**************************************/
 int filter( const struct dirent *dir )
/**************************************/

{
   int len = strlen( dir->d_name ) ;

   if( len < 4 )
      return 0 ;

   return strcmp(dir->d_name+len-4,".txt") == 0 ;
}

/**********/
 int main()
/**********/

{
   struct dirent **namelist ;
   int             count ;

   count = scandir( ".", &namelist, filter, alphasort ) ;

   if( count >= 0 )
   {
      for( int i = 0; i < count; i++ )
      {
         printf( "%s\n", namelist[i]->d_name ) ;
         free( namelist[i] ) ;
      }

      free( namelist ) ;
   }

   return 0 ;
}

int filter( const struct dirent *dir )

Die Funktion filter ist eigentlich alles was ich wirklich geschrieben habe, der ganze Rest wird in der man-Page recht gut erklärt.

filter bekommt einen Directory-Eintrag, und entscheidet, ob er in die Liste aufgenommen werden soll. Hier stellt sie sicher, dass der Name mindestens 4 Zeichen lang ist, und prüft dann ob die Extension ".txt" heisst.

In meinem Fall bin ich sicher, dass die gefundenen Einträge tatsächlich Files sind. Im allgemeinen Fall müssten die Flags im dirent geprüft werden, damit nicht ein Directory oder eine Named Pipe in die Liste aufgenommen wird.

Anstelle der Filterfunktion kann auch einfach NULL angegeben werden, dann nimmt scandir alle Einträge im Directory in die Liste auf.

alphasort

alphasort ist ebenfalls eine Funktion, und gehört zur Library. Bei speziellen Anforderungen könnte hier eine eigene Funktion angegeben werden.

Memory Management

Das zweite Argument von scandir ist ein Dreifach-Pointer!

Dreifach-Pointer sind sehr, sehr selten, aber hier wird einer gebraucht, weil scandir ein Array von Pointern im aufrufenden Programm anlegt. namelist gibt die Adresse an, wo scandir den Pointer auf das Array speichern soll.

Nach dem Aufruf der Funktion muss der Entwickler wieder sauber aufräumen. In der for-Schleife werden die einzelnen C-Strings freigegeben und nach der Schleife dann das Array mit den Pointern.

Selber ausprobieren

Sie können den Code herunterladen und damit herumspielen.