Dos paquetes se reparten el trabajo: os crea y lista, y path/filepath recorre árboles y compone rutas. Usar filepath en vez de pegar cadenas con barras es lo que hace que el mismo código funcione en Linux y en Windows.
También te puede interesar
Crear
fmt.Println(os.Mkdir("salida/informes", 0755)) // falta el padre
fmt.Println(os.MkdirAll("salida/informes", 0755))
fmt.Println(os.MkdirAll("salida/informes", 0755)) // y otra vez
salidamkdir salida/informes: no such file or directory <nil> <nil>
La diferencia es la de siempre: Mkdir crea una sola carpeta y exige que la que la contiene exista; MkdirAll crea todo lo que falte. Y MkdirAll no protesta si ya existe, lo que permite llamarla sin comprobar nada antes.
Listar un nivel
ent, err := os.ReadDir("salida")
if err != nil {
return err
}
for _, e := range ent {
tipo := "archivo"
if e.IsDir() {
tipo = "carpeta"
}
fmt.Printf("%-8s %s\n", tipo, e.Name())
}
salidacarpeta informes archivo notas.txt
os.ReadDir devuelve la lista ya ordenada por nombre. Cada entrada trae el nombre y si es carpeta sin coste extra; para el tamaño o la fecha hay que pedir e.Info(), que sí consulta el disco.
Recorrer el árbol entero
err := filepath.WalkDir("salida", func(ruta string, d fs.DirEntry, err error) error {
if err != nil {
return err // un error al entrar en esa ruta
}
if d.IsDir() {
fmt.Println("[dir]", ruta)
return nil
}
info, _ := d.Info()
fmt.Printf(" %-32s %4d bytes\n", ruta, info.Size())
return nil
})
salida[dir] salida
[dir] salida/informes
salida/informes/enero.csv 4 bytes
salida/informes/febrero.csv 4 bytes
[dir] salida/informes/viejos
salida/informes/viejos/2024.csv 4 bytes
salida/notas.txt 5 bytes
La función se llama una vez por cada archivo y cada carpeta. Devolver nil continúa; devolver un error corta el recorrido entero.
Saltarse una carpeta
if d.IsDir() && d.Name() == "viejos" {
return filepath.SkipDir
}
salidasalida/informes/enero.csv salida/informes/febrero.csv salida/notas.txt
filepath.SkipDir es un error especial que no es un error: le dice al recorrido «esta carpeta entera, no». Es como se ignoran los node_modules y los .git del mundo.
WalkDir y no Walk. La función antigua, filepath.Walk, pedía la información completa de cada archivo aunque no la usaras, y en árboles grandes eso se nota mucho. WalkDir solo la pide si se la pides tú con d.Info().Filtrar por patrón
coincide, _ := filepath.Glob("salida/informes/*.csv")
for _, c := range coincide {
fmt.Println(c)
}
salidasalida/informes/enero.csv salida/informes/febrero.csv
Glob no entra en subcarpetas: el * no cruza barras. Para buscar en todo el árbol hay que combinarlo con WalkDir y filepath.Match.
Rutas
r := filepath.Join("salida", "informes", "enero.csv")
fmt.Println("Join ->", r)
fmt.Println("Dir ->", filepath.Dir(r))
fmt.Println("Base ->", filepath.Base(r))
fmt.Println("Ext ->", filepath.Ext(r))
abs, _ := filepath.Abs(r)
fmt.Println("Abs ->", abs)
salidaJoin -> salida/informes/enero.csv Dir -> salida/informes Base -> enero.csv Ext -> .csv Abs -> /home/ana/w/dirs/salida/informes/enero.csv
+ "/" +. filepath.Join usa el separador del sistema, limpia las barras repetidas y resuelve los ... En Windows el mismo código produce salida\informes\enero.csv sin que tengas que hacer nada. Ojo con el paquete parecido path, a secas: ese es solo para URL y usa siempre la barra inclinada.Todo el código se compiló y ejecutó con Go 1.25 antes de publicarla; las salidas están copiadas de esa ejecución. Documentación oficial: os y path/filepath.