Author Topic: Listar contenido de un directorio usando funciones POSIX de C  (Read 3455 times)

yoel

  • Newbie
  • *
  • Posts: 7
  • Karma: 0
    • View Profile
Listar contenido de un directorio usando funciones POSIX de C
« on: Febrero 10, 2014, 02:49:05 am »
En varios SO existe un comando de consola para listar el contenido de un directorio. Es el caso de dir en DOS y ls en UNIX. Como este es un foro de GNU/Linux, nos interesamos en ls.

Ahora bien, el problema es que queremos realizar esta misma función, pero sin recurrir a bash, sino solamente en C. Ojo, no es poner system("ls"), que sería una forma encubierta de llamar al procesador de comandos bash.

Por suerte existe el estándar POSIX, que define una API o conjunto de funciones para interactuar con al sistema operativo, independientemente de la plataforma y acorde a los requerimientos de IEEE. Al hablar de "estándar", quiere decir que estas funciones deben funcionar uniformemente en distintos sistemas, incluso algunos compiladores como gcc/g++ incluyen bibliotecas de funciones POSIX para el (rufián de la informática) Windows. Para una rápida lectura de este tópico, consultar http://es.wikipedia.org/wiki/POSIX

En el caso de listar el contenido de un directorio, existen las funciones opendir, closedir, readdir, que requieren el fichero de cabecera <dirent.h>. Para más información véase la documentación oficial http://manpages.ubuntu.com/manpages/hardy/es/man3/readdir.3.html

Abrir y observar el contenido de un directorio requiere al menos dos variables, una de tipo DIR *, es decir apuntador a directorio, y otra de tipo struct dirent *, es decir un apuntador a una estructura dirent.
La estructura dirent es definida por POSIX y exige un campo de ella llamado d_name de tipo char * (string) que contiene el nombre de cada fichero que sea leído en el directorio.

Así para comenzar, declaramos las variables:
Code: [Select]
const char * dir_name;            /* nombre del directorio */
DIR * dir_ptr = NULL;
struct dirent * dirent_ptr;

donde dir_name es para contener el nombre del directorio. A continuación:

Code: [Select]
dir_ptr = opendir( dir_name )

donde una llamada exitosa devolverá un apuntador no nulo al directorio, y una llamada fallida devolverá NULL. Luego, cada llamada sucesiva a la función readdir devolverá un apuntador a estructura dirent, cuyo miembro d_name es el nombre del fichero. Cuando no haya más ficheros, readdir devolverá NULL. Es decir, hay que llamar a readdir por cada fichero que exista, hasta el final. Ponemos poner por prevención una variable contador que limite el número de archivos a 100. Con ésto, una sentencia para listar todos los ficheros en el directorio sería:

Code: [Select]
while ( count < 100 && ( dirent_ptr = readdir( dir_ptr ) ) != NULL ) {
printf( "[%03d] %s\n", ++count, dirent_ptr -> d_name );
}

Y es todo. Al final, hay que cerrar el directorio con closedir( dir_ptr ).

Ahora, se me ha ocurrido hacer un ejecutable para ser invocado desde la línea de comandos (un mini-clon de ls) al que sea pasado como argumento el nombre del directorio que queremos observar. Si se pasan menos o más de un argumento, devuelve un mesaje de error. El código completo sería:

Code: [Select]
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>

/* Lista el contenido de un directorio, pasado como argumento del
 * programa. Basado en funciones POSIX de la biblioteca GNU de C.
 */
int main( int argc, char ** argv ) {

const char * dir_name;
DIR * dir_ptr = NULL;
struct dirent * dirent_ptr;
int count = 0;

/* comprueba que se haya pasado un único argumento */
if ( argc != 2 ) {
printf( "Use list <directorio>\n" );
return -1;
}

/* comprueba si directorio existe, y tiene permisos para abrirlo */
dir_name = argv[1];
if ( ( dir_ptr = opendir( dir_name ) ) == NULL ) {
printf( "No existe o no se pudo abrir el directorio '%s'\n", dir_name );
return -1;
}

/* ahora listamos el directorio */
while ( count < 100 && ( dirent_ptr = readdir( dir_ptr ) ) != NULL ) {
printf( "[%03d] %s\n", ++count, dirent_ptr -> d_name );
}

/* cierra el directorio */
if ( dir_ptr != NULL ) closedir( dir_ptr );

        /* bye .... */
        return 0;
}

Ahora compilen
Code: [Select]
gcc -o list list.c

y hagan por ejemplo desde la línea de comandos
Code: [Select]
list .
y verán el contenido del directorio actual. Por supuesto que es muy limitado en comparación con ls. Por ejemplo, no imprime atributos ni tipo de fichero, tampoco maneja salida en colores. Pero es algo bueno para comenzar e ilustrar el uso de las funciones POSIX.

Que lo disfruten ...
« Last Edit: Febrero 11, 2014, 09:44:57 am by yoel »

Kasey00

  • Newbie
  • *
  • Posts: 3
  • Karma: 0
    • View Profile
Re: Listar contenido de un directorio usando funciones POSIX de C
« Reply #1 on: Enero 10, 2020, 10:54:49 pm »
En varios SO existe un comando de consola para listar el contenido de un directorio. Es el caso de dir en DOS y ls en UNIX. Como este es un foro de GNU/Linux, nos interesamos en ls.

Ahora bien, el problema es que queremos realizar esta misma función, pero sin recurrir a bash, sino solamente en C. Ojo, no es poner system("ls"), que sería una forma encubierta de llamar al procesador de comandos bash.

Por suerte existe el estándar POSIX, que define una API o conjunto de funciones para interactuar con al sistema operativo, independientemente de la plataforma y acorde a los requerimientos de IEEE. Al hablar de "estándar", quiere decir que estas funciones deben funcionar uniformemente en distintos sistemas, incluso algunos compiladores como gcc/g++ incluyen bibliotecas de funciones POSIX para el (rufián de la informática) Windows. Para una rápida lectura de este tópico, consultar ipage

En el caso de listar el contenido de un directorio, existen las funciones opendir, closedir, readdir, que requieren el fichero de cabecera <dirent.h>. Para más información véase la documentación oficial http://manpages.ubuntu.com/manpages/hardy/es/man3/readdir.3.html

Abrir y observar el contenido de un directorio requiere al menos dos variables, una de tipo DIR *, es decir apuntador a directorio, y otra de tipo struct dirent *, es decir un apuntador a una estructura dirent.
La estructura dirent es definida por POSIX y exige un campo de ella llamado d_name de tipo char * (string) que contiene el nombre de cada fichero que sea leído en el directorio.

Así para comenzar, declaramos las variables:
Code: [Select]
const char * dir_name;            /* nombre del directorio */
DIR * dir_ptr = NULL;
struct dirent * dirent_ptr;

donde dir_name es para contener el nombre del directorio. A continuación:

Code: [Select]
dir_ptr = opendir( dir_name )

donde una llamada exitosa devolverá un apuntador no nulo al directorio, y una llamada fallida devolverá NULL. Luego, cada llamada sucesiva a la función readdir devolverá un apuntador a estructura dirent, cuyo miembro d_name es el nombre del fichero. Cuando no haya más ficheros, readdir devolverá NULL. Es decir, hay que llamar a readdir por cada fichero que exista, hasta el final. Ponemos poner por prevención una variable contador que limite el número de archivos a 100. Con ésto, una sentencia para listar todos los ficheros en el directorio sería:

Code: [Select]
while ( count < 100 && ( dirent_ptr = readdir( dir_ptr ) ) != NULL ) {
printf( "[%03d] %s\n", ++count, dirent_ptr -> d_name );
}

Y es todo. Al final, hay que cerrar el directorio con closedir( dir_ptr ).

Ahora, se me ha ocurrido hacer un ejecutable para ser invocado desde la línea de comandos (un mini-clon de ls) al que sea pasado como argumento el nombre del directorio que queremos observar. Si se pasan menos o más de un argumento, devuelve un mesaje de error. El código completo sería:

Code: [Select]
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>

/* Lista el contenido de un directorio, pasado como argumento del
 * programa. Basado en funciones POSIX de la biblioteca GNU de C.
 */
int main( int argc, char ** argv ) {

const char * dir_name;
DIR * dir_ptr = NULL;
struct dirent * dirent_ptr;
int count = 0;

/* comprueba que se haya pasado un único argumento */
if ( argc != 2 ) {
printf( "Use list <directorio>\n" );
return -1;
}

/* comprueba si directorio existe, y tiene permisos para abrirlo */
dir_name = argv[1];
if ( ( dir_ptr = opendir( dir_name ) ) == NULL ) {
printf( "No existe o no se pudo abrir el directorio '%s'\n", dir_name );
return -1;
}

/* ahora listamos el directorio */
while ( count < 100 && ( dirent_ptr = readdir( dir_ptr ) ) != NULL ) {
printf( "[%03d] %s\n", ++count, dirent_ptr -> d_name );
}

/* cierra el directorio */
if ( dir_ptr != NULL ) closedir( dir_ptr );

        /* bye .... */
        return 0;
}

Ahora compilen
Code: [Select]
gcc -o list list.c

y hagan por ejemplo desde la línea de comandos
Code: [Select]
list .
y verán el contenido del directorio actual. Por supuesto que es muy limitado en comparación con ls. Por ejemplo, no imprime atributos ni tipo de fichero, tampoco maneja salida en colores. Pero es algo bueno para comenzar e ilustrar el uso de las funciones POSIX.

Que lo disfruten ...

Muchas gracias por esto. Realmente un placer para los programadores.
« Last Edit: Enero 12, 2020, 02:19:37 pm by Kasey00 »

 

ey