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"); |