Directorios

Directorios

Obtener el directorio actual en Zig

Este tipo de dato nos puede ser útil para distintas tareas. Está bien saber en que directorio estamos en cada momento y actuar en consecuencia.

get_current_dir.zig

const std = @import("std");

const print = std.debug.print;

pub fn main() void {

    // obtenemos los datos del cwd (current working dir)

    const o_cwd = std.fs.cwd();

    // preparamos un buffer que pueda guardar la respuesta

    var a_buff: [std.fs.max_path_bytes]u8 = undefined;

    // miramos el path real del . (el dir actual) y capturamos el error

    if (o_cwd.realpath(".", &a_buff)) |s_path| {

        print("Path {s}\n", .{s_path});

    } else |err_x| {

        print("Error {any}\n", .{err_x});

    }

}

$ zig run get_current_dir.zig

Path /home/zenofzig/code/9

Estamos usando un array como buffer para guardar la respuesta de la función realpath:

 // preparamos un buffer que pueda guardar la respuesta

 var a_buff: [std.fs.max_path_bytes]u8 = undefined;

La longitud máxima que puede usar un nombre del path está definida en std.fs.max_path_bytes y depende de cada plataforma.

Como alternativa a la función realpath podemos usar realpathAlloc que necesita de un allocator de memoria.

get_current_dir_alloc.zig

const std = @import("std");

const print = std.debug.print;

pub fn main() !void {

    // obtenemos los datos del cwd (current working dir)

    const o_cwd = std.fs.cwd();

    const o_alloc = std.heap.page_allocator;

    // miramos el path real del . (el dir actual) y capturamos el error

    if (o_cwd.realpathAlloc(o_alloc, ".")) |s_path| {

        // liberamos la memoria del s_path aunque en este caso

        // como el programa es lineal

        // la liberaría al final del main

        defer o_alloc.free(s_path);

        print("Path {s}\n", .{s_path});

    } else |err_x| {

        print("Error {any}\n", .{err_x});

    }

}

$ zig run get_current_dir_alloc.zig

Path /home/zenofzig/code/9

Comprobar acceso a un directorio

Si solo queremos saber si existe un directorio y si tenemos acceso a él podríamos usar algo así:

check_dir.zig

const std = @import("std");

const print = std.debug.print;

fn check_dir(s_dir: []const u8) !void {

    var o_dir = try std.fs.openDirAbsolute(

        s_dir,

        .{},

    );

    defer o_dir.close();

    try o_dir.access(".", .{});

}

pub fn main() void {

    const s_dir = "/root/";

    const s_msg = if (check_dir(s_dir))

        "Existe"

    else |err_x|

        @errorName(err_x);

    print("{s}\n", .{s_msg});

}

$ zig run check_dir.zig

AccessDenied

Iterar sobre un directorio

        En el siguiente ejemplo recorremos las entradas de un directorio e imprimimos sus entradas. Usamos la función std.fs.Dir.iterate o iterateAssumeFirstIteration. Esta última la usaremos cuando sabemos que no hemos iterado antes sobre el dir. Por ejemplo: es la primera iteración es porque acabamos de abrir el directorio.

Lo importante de estas funciones es que nos devuelven un iterador.

        Usamos el iterador i_dir llamando su método .next() para pasar al siguiente elemento del directorio, si existe. Es un caso ideal para listar el contenido del directorio usando nuestro fiel bucle while:

list_dir.zig

const std = @import("std");

const print = std.debug.print;

pub fn main() !void {

    const s_dir = "/";

    // abrimos el directorio

    var o_dir = std.fs.openDirAbsolute(

        s_dir,

        // flag necesario para poder iterar sobre el dir

        .{ .iterate = true },

    ) catch |err_x| {

        // si no existe o hay otro error:

        print("Error: {s} \n", .{@errorName(err_x)});

        return;

    };

    // cerramos el dir

    defer o_dir.close();

    // iterator para poder recorrer las entradas del dir

    var i_dir = o_dir.iterateAssumeFirstIteration();

    // mientras haya entradas en el dir las imprimimos

    while (try i_dir.next()) |o_entry| {

        print("{s}\n", .{o_entry.name});

    }

}

$ zig run list_dir.zig

srv

dev

tmp

...

Cuando abrimos el directorio, para poder crear un iterador necesitamos indicar de manera explícita el flag del segundo argumento .iterate = true.

A lo mejor te preguntas: ¿pero y cómo sé qué flag tengo que poner para poder iterar? ¿Y cómo puedo saber otras peculiaridades de una función u objeto?

Antes en el libro vimos como hacer esto con .empty:

https://zenofzig.com/libro/c7-listas-dinamicas-arraylist.html. Para no repetirme y abreviar: en todos los lenguajes es importante mirar la documentación del código fuente.

Si miras la declaración de la función openDirAbsolute verás esto:

fs.zig

 pub fn openDirAbsolute(

     absolute_path: []const u8, 

     flags: Dir.OpenOptions) File.OpenError!Dir {

 // ...

Si te fijas en esos flags: Dir.OpenOptions

Dir.zig

 pub const OpenOptions = struct {

    /// ...

    /// `true` means the opened directory

    /// can be scanned for the files and sub-directories

    /// of the result. It means the `iterate` function can be called.

    iterate: bool = false,

    /// ...

};

Esta documentación explica que, para poder iterar sobre las entradas de un directorio, al abrirlo,  necesitamos poner .iterate = true.

Además si omites este flag el programa crasheará con este mensaje:

.BADF => unreachable, // Dir is invalid or was opened without iteration ability

Solo te puedo recomendar que visites aquellas funciones que vas a usar y leas su documentación aunque sea por encima. Al operar con ficheros y directorios, ten cuidado: no sea que borres o sobreescribas algo que no deberías. Otra recomendación: haz tus pruebas en el directorio temporal /tmp/ mientras estás aprendiendo.

Crear y borrar un directorio

        AVISO: Si ejecutas código en los playgrounds de zenofzig.com no hay ningún peligro de borrar algo que no deberías. Si ejecutas código que borra ficheros o directorios en tu propia máquina (no en el browser), asegúrate de entender bien lo que estás haciendo antes de ejecutarlo. Potencialmente podrías borrar algo importante, así que precaución.

Para crear un directorio nuevo en una ruta absoluta:

create_dir.zig

const std = @import("std");

const print = std.debug.print;

pub fn main() !void {

    // crear un dir en ruta absoluta

    try std.fs.makeDirAbsolute("/tmp/newdir");

}

$ zig run create_dir.zig

...

Si intentas ejecutar varias veces este

Para borrar ese mismo directorio usaríamos:

 // borrar un dir

 try std.fs.deleteDirAbsolute("/tmp/newdir");


Ficheros
Resumen del capítulo
© 2025 Zen of Zig