Información de un archivo en Go

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.

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
El 0 inicial no es decorativo. 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.