Casi todo sale de una sola llamada, os.Stat, que devuelve una ficha con el tamaño, los permisos, la fecha y el tipo. La única sutileza es qué pasa con los enlaces simbólicos, y ahí entra su hermana os.Lstat.
También te puede interesar
Lo básico
info, err := os.Stat("inventario.csv")
if err != nil {
return err
}
fmt.Println("nombre :", info.Name())
fmt.Println("tamaño :", info.Size(), "bytes")
fmt.Println("permisos :", info.Mode().Perm())
fmt.Println("modificado:", info.ModTime().Format("2006-01-02 15:04"))
fmt.Println("¿carpeta? :", info.IsDir())
salidanombre : inventario.csv tamaño : 41 bytes permisos : -rw-r----- modificado: 2026-10-06 04:43 ¿carpeta? : false
Go guarda solo la fecha de modificación. La de creación no es portable —no existe en todos los sistemas de archivos— y por eso no está en la interfaz.
Qué es cada cosa
i, err := os.Lstat(ruta) // Lstat NO sigue los enlaces
if err != nil {
return err
}
switch {
case i.Mode().IsRegular():
tipo = "archivo normal"
case i.IsDir():
tipo = "carpeta"
case i.Mode()&fs.ModeSymlink != 0:
tipo = "enlace simbólico"
default:
tipo = "otra cosa"
}
salidainventario.csv archivo normal datos carpeta ultimo.csv enlace simbólico
El Mode() mezcla el tipo y los permisos en un solo valor. Por eso para el tipo se comprueba con & contra las constantes de fs, y para los permisos se usa Perm(), que se queda con los nueve bits de abajo.
Stat o Lstat
s, _ := os.Stat("ultimo.csv") // sigue el enlace
l, _ := os.Lstat("ultimo.csv") // se queda en el enlace
fmt.Println("Stat dice", s.Size(), "bytes (el del destino)")
fmt.Println("Lstat dice", l.Size(), "bytes (el del propio enlace)")
destino, _ := os.Readlink("ultimo.csv")
fmt.Println("apunta a:", destino)
salidaStat dice 41 bytes (el del destino) Lstat dice 14 bytes (el del propio enlace) apunta a: inventario.csv
Esos 14 bytes del enlace son la longitud del texto inventario.csv, que es literalmente lo que el enlace guarda. La regla práctica: usa Stat casi siempre, y Lstat solo cuando te importe distinguir el enlace de su destino, como al recorrer un árbol y querer evitar bucles.
Cuando no existe
_, err := os.Stat("fantasma.txt")
fmt.Println(err)
fmt.Println(errors.Is(err, fs.ErrNotExist))
salidastat fantasma.txt: no such file or directory true
Cambiar permisos y fechas
os.Chmod("inventario.csv", 0600)
os.Chtimes("inventario.csv", ayer, ayer) // acceso y modificación
salidaahora: -rw------- fecha: 2026-10-05
0600 en octal son los permisos que esperas; 600 en decimal son otros completamente distintos. En Go moderno se puede escribir 0o600, que deja más claro de qué base se habla.Datos del sistema, si hacen falta
if st, ok := info.Sys().(*syscall.Stat_t); ok {
fmt.Println("inodo:", st.Ino, " enlaces:", st.Nlink, " uid:", st.Uid)
}
salidainodo: 27559420 enlaces: 1 uid: 1000
Sys() da acceso a la estructura del sistema operativo, con cosas que no están en la interfaz portable. Nótese la aserción de tipo con ok: en Windows eso no es un *syscall.Stat_t y el código tiene que sobrevivir a ello.
Resumen
| Quiero saber | Llamo a |
|---|---|
| Tamaño, fecha, permisos | os.Stat(ruta) |
| Si es carpeta | info.IsDir() |
| Si es un enlace | os.Lstat y Mode()&fs.ModeSymlink |
| A dónde apunta el enlace | os.Readlink(ruta) |
| Si existe | os.Stat + errors.Is(err, fs.ErrNotExist) |
| Cambiar permisos | os.Chmod(ruta, 0600) |
| Cambiar la fecha | os.Chtimes(ruta, acceso, modif) |
Todo el código se compiló y ejecutó con Go 1.25 antes de publicarla, sobre archivos creados en el momento; las salidas están copiadas de esa ejecución. Documentación oficial: os.Stat y fs.FileInfo.