← Sistemas de Archivos y E/S Bloque 1 · Archivos y sistemas de archivos · Tema 3 de 50

Tema 3

Explorar con ls, stat, df y du

Un archivo muestra 64 MiB en ls, pero du apenas le atribuye espacio. Borras un registro y df sigue mostrando el volumen ocupado. Una carpeta parece medir 4096 bytes aunque contiene varios gigabytes. Estas observaciones tienen explicación: cada herramienta responde una pregunta diferente. En este tema aprenderás a leer sus columnas y unidades, crearás archivos para medir esas diferencias, comprobarás cómo afectan los enlaces y los archivos abiertos, escribirás un informe en Bash y usarás un simulador para relacionar la contabilidad del volumen con la de un recorrido de directorios.

1. Cuatro herramientas, cuatro preguntas

En el tema 2 distinguimos el nombre, el objeto y sus metadatos. Ahora utilizaremos esa separación para observar un sistema real desde la terminal. No hace falta escribir todavía un programa en C: las herramientas de Bash permiten construir intuición y después verificar lo que hagan nuestros programas.

HerramientaPregunta principalAlcance
ls¿Qué nombres hay y qué atributos básicos tienen?Los objetos indicados o las entradas de un directorio.
stat¿Qué metadatos tiene este objeto?Una ficha por ruta; con otras opciones, una ficha del sistema de archivos.
df¿Cuánto espacio y cuántos inodos informa este sistema de archivos?El sistema de archivos que contiene la ruta indicada.
du¿Cuánto almacenamiento se atribuye a los objetos encontrados en este recorrido?Las rutas indicadas y sus descendientes, según las opciones.

Antes de comparar dos cifras, comprueba tres cosas: qué magnitud miden, qué unidad usan y qué conjunto de objetos incluyen. Si alguna difiere, no tienes por qué obtener el mismo resultado.

ls -l archivo suele mostrar su longitud lógica en bytes. du archivo informa normalmente de almacenamiento asignado, expresado en una unidad de presentación. df archivo informa del volumen que contiene el archivo, incluyendo mucho más que ese documento. Pasarles la misma ruta no convierte sus resultados en equivalentes.

Unidades que conviene fijar desde el principio

Usaremos KiB = 1024 bytes, MiB = 1024² bytes y GiB = 1024³ bytes. Las unidades decimales son kB = 1000, MB = 1000² y GB = 1000³. La diferencia crece con el tamaño: 1 GB son aproximadamente 0,931 GiB. No ha desaparecido espacio por cambiar la unidad.

En las herramientas compatibles con GNU, -h presenta cantidades en potencias de 1024 y suele escribir sufijos como K, M y G. --si utiliza potencias de 1000. Para comparar números sin redondeo humano utilizaremos -B1 cuando corresponda. Esa opción cambia la unidad de presentación; no reformatea el volumen ni cambia cómo se asignan sus bloques.

Entorno de los ejemplos

El tema usa Bash en Linux y opciones de GNU Coreutils. Otras implementaciones pueden ofrecer las mismas opciones con diferencias menores; compruébalas con --help y --version. macOS y BusyBox no admiten necesariamente las mismas opciones. Las salidas son ilustrativas salvo cuando se indica una longitud exacta creada por nosotros.

2. Preparar un laboratorio reproducible

Trabaja en una terminal de Linux o de WSL. En WSL usa preferentemente el directorio personal de la distribución para observar el comportamiento de un sistema de archivos Linux. Los experimentos crean una carpeta nueva con unas decenas de MiB de datos; no necesitan sudo, particiones adicionales ni formatear dispositivos.

terminal · archivos de longitudes conocidas
$ LAB3=$(mktemp -d "$HOME/tema3.XXXXXX")
$ cd "$LAB3"
$ mkdir -p datos registros .cache
$ printf 'ABC\n' > datos/notas.txt
$ touch datos/vacio
$ head -c 5000 /dev/zero > datos/mediano.bin
$ dd if=/dev/zero of=registros/app.log bs=1M count=2 status=none
$ dd if=/dev/zero of=.cache/temporal.bin bs=1M count=1 status=none
$ printf 'informe\n' > 'datos/informe final.txt'
$ printf '%s\n' "$LAB3"
/home/ana/tema3.A1b2C3

El usuario y el sufijo de la última línea cambiarán. Mantén la terminal para conservar LAB3. head -c 5000 escribe exactamente 5000 bytes y, en GNU dd, bs=1M significa bloques de 1.048.576 bytes. El archivo notas.txt tiene cuatro bytes y informe final.txt, ocho, contando sus saltos de línea.

Para repetir los comandos sin que un alias añada opciones, puedes anteponer command, como en command ls -l. Cuando nos interesen encabezados y nombres de tipo en inglés fijaremos LC_ALL=C solo para ese comando. Las longitudes que creamos no dependen de la traducción de la herramienta.

Una foto inicial

terminal · mirar el mismo lugar con cuatro alcances
$ ls -la
$ stat datos/notas.txt
$ df -h .
$ du -sh .

La carpeta recién creada ocupa muy poco frente al sistema de archivos completo. Si df muestra cientos de gigabytes utilizados, no se los atribuyas al laboratorio: describe todo el sistema de archivos que contiene el punto .. Guardaremos esta distinción para todos los experimentos siguientes.

3. ls: nombres y una primera ficha

Sin argumentos, ls lista las entradas no ocultas del directorio actual. Con una ruta a un directorio, normalmente lista su contenido. Con una ruta a un archivo, muestra ese objeto. La opción -l solicita el formato largo:

terminal · anatomía de una línea
$ LC_ALL=C ls -l datos/notas.txt
-rw-r--r-- 1 ana ana 4 Oct 29 10:00 datos/notas.txt
│          │ │   │   │ └──────────┘ └─────────────┘
│          │ │   │   │     fecha          nombre
│          │ │   │   longitud lógica en bytes
│          │ │   grupo
│          │ propietario
│          enlaces duros
tipo y permisos

Los nombres de usuario, permisos y fechas son ejemplos. La cifra 4 sí procede del contenido que escribimos. El primer carácter - identifica un archivo regular; para un directorio aparece d, y para un enlace simbólico, l. Un carácter adicional como + puede señalar información de control de acceso que no cabe en los nueve bits de permisos.

La fecha de ls -l es normalmente mtime, la modificación de contenido. Su presentación puede omitir el año para fechas recientes y mostrarlo para otras. No uses esa salida abreviada para comparar marcas temporales con precisión: puedes solicitar --full-time o consultar stat.

Opciones para observar, ordenar y distinguir

OrdenQué aportaPrecaución al leerla
ls -laFormato largo, entradas ocultas, punto y punto-punto.Incluir nombres ocultos no modifica sus permisos.
ls -lAIncluye ocultos y omite . y ...Es útil para una vista humana menos repetitiva.
ls -lhLongitudes con sufijos legibles.Las cantidades están redondeadas.
ls -ld datosLa ficha del directorio mismo.Su tamaño no suma los documentos que contiene.
ls -li datosNúmeros de inodo junto a los atributos.Los inodos son locales a cada sistema de archivos.
ls -lS datosOrden por longitud, mayor primero.Es longitud lógica, no almacenamiento asignado.
ls -lt registrosOrden por mtime, más reciente primero.Una fecha reciente no significa que el objeto sea nuevo.
ls -ln datosUID y GID numéricos.Evita confundir un identificador con el nombre que resuelve el sistema.

Qué significan total y la opción -s

Al listar un directorio en formato largo aparece a menudo una línea total. Resume la asignación de las entradas listadas, con las reglas y unidades de la herramienta; no es la suma recursiva de toda la carpeta. La opción -s añade el almacenamiento asignado de cada entrada, mientras la columna de tamaño de -l conserva la longitud lógica.

terminal · hacer explícita la unidad de los bloques mostrados
$ LC_ALL=C ls -lsk datos/notas.txt datos/mediano.bin datos/vacio
8 -rw-r--r-- 1 ana ana 5000 Oct 29 10:00 datos/mediano.bin
4 -rw-r--r-- 1 ana ana    4 Oct 29 10:00 datos/notas.txt
0 -rw-r--r-- 1 ana ana    0 Oct 29 10:00 datos/vacio

En este ejemplo de asignación con bloques de 4 KiB, el primer número está expresado en KiB por -k: 8 KiB asignados para el archivo de 5000 bytes y 4 KiB para el de cuatro. La cifra exacta de asignación puede cambiar en otros sistemas. -k fija las unidades de esa cuenta de bloques y no cambia la longitud de 5000 a 5.

Por qué no conviene programar leyendo columnas de ls

La salida de ls está orientada a las personas: fechas, comillas, columnas, colores y nombres con espacios complican extraer campos. Un nombre incluso puede contener un salto de línea. No construyas un programa que asuma «la novena palabra siempre es el nombre». Para consultar una ruta usa un formato de stat; para recorrer nombres, usa herramientas como find -print0 y lectores que admitan separadores nulos.

También hay que separar lo que hace Bash de lo que hace ls. En ls datos/*, el shell expande el comodín antes de ejecutar la orden y normalmente no incluye nombres que comienzan por punto. ls -A datos, en cambio, pide a la herramienta que examine ese directorio incluyendo sus entradas ocultas.

4. stat: atributos exactos y unidades

stat sirve para comprobar un atributo sin deducirlo del formato de ls. Su salida completa es una buena ficha de inspección; -c permite elegir los campos y produce una línea por argumento:

terminal · separar longitud, asignación e identidad
$ LC_ALL=C stat -c '%n | tipo=%F | bytes=%s | bloques=%b | unidad=%B' datos/notas.txt datos/mediano.bin datos/vacio
datos/notas.txt | tipo=regular file | bytes=4 | bloques=8 | unidad=512
datos/mediano.bin | tipo=regular file | bytes=5000 | bloques=16 | unidad=512
datos/vacio | tipo=regular empty file | bytes=0 | bloques=0 | unidad=512
$ stat -c '%n | dev=%d ino=%i enlaces=%h modo=%a uid=%u gid=%g' datos/notas.txt
$ stat -c 'acceso=%x | modificacion=%y | cambio=%z | nacimiento=%w' datos/notas.txt

La asignación ilustrada coincide con el caso de bloques de 4 KiB de la sección anterior. La descripción del archivo vacío puede variar entre implementaciones. Para calcular los bytes asignados que comunica esta ficha se multiplica %b × %B: 16 × 512 = 8192 para el de 5000 bytes.

FormatoMagnitudInterpretación
%sTamaño lógico en bytes.Hasta dónde llega el contenido de un archivo regular.
%bNúmero de unidades asignadas.Multiplicarlo por %B para obtener bytes.
%BBytes de cada unidad de %b.En estos ejemplos de Linux, 512.
%oTamaño de transferencia aconsejado.No sustituye a %B en la multiplicación.
%d, %iDispositivo e inodo.Ayudan a detectar nombres que comparten un objeto.
%hContador de enlaces duros.No cuenta los enlaces simbólicos que llegan al destino.
%x, %y, %zatime, mtime y ctime.Acceso, contenido y estado; ctime no es creación.

Una comprobación con aritmética de Bash

terminal · dos números y sus unidades
$ read -r bloques unidad < <(stat -c '%b %B' datos/mediano.bin)
$ printf 'Bytes asignados: %s\n' "$((bloques * unidad))"
Bytes asignados: 8192
$ du -B1 datos/mediano.bin
8192    datos/mediano.bin

La construcción < <(...) es una sustitución de procesos de Bash: entrega a read la salida de stat. Aquí leemos solo dos números y comprobamos una ruta que sabemos que existe. En un script general también hay que comprobar si la consulta falló. El informe posterior lo hará.

Un objeto y el sistema de archivos que lo contiene

stat -f . cambia de nivel y consulta el sistema de archivos, no la ficha ordinaria del directorio. Los formatos también cambian de significado. Por ejemplo, con -f, %b es el número total de bloques del sistema y %S la unidad fundamental para esas cuentas:

terminal · contadores del sistema de archivos
$ stat -f -c 'tipo=%T unidad=%S total=%b libres=%f disponibles=%a' .

No mezcles el %b de stat archivo con el de stat -f ruta. Uno cuenta asignación del objeto y el otro capacidad del sistema de archivos. Veremos a continuación que «libre» y «disponible» tampoco son siempre la misma cantidad.

5. df: la contabilidad del sistema de archivos

df consulta los contadores del sistema de archivos. No necesita visitar todos sus documentos para sumar tamaños. La orden df -h . selecciona el sistema de archivos que contiene el directorio actual y presenta su capacidad en formato legible.

terminal · un ejemplo de volumen con reserva
$ LC_ALL=C df -h .
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda2       100G   60G   35G  64% /
$ df -T .
$ df -B1 --output=source,fstype,size,used,avail,pcent,target .

La salida de 100G es un ejemplo conceptual, no una predicción de tu equipo. Filesystem identifica la fuente del montaje; Mounted on señala dónde se incorpora al árbol. Size, Used y Avail proceden de contadores del volumen, expresados en la unidad solicitada. -T añade el tipo de sistema de archivos y --output permite escoger columnas.

Por qué tamaño menos usado puede superar disponible

Un sistema de archivos puede mantener bloques libres que no estén disponibles para asignaciones ordinarias. Una reserva para el administrador es una causa habitual. En el ejemplo quedan 40 GiB sin usar, pero solo 35 GiB figuran como disponibles. Los 5 GiB restantes no se han convertido por ello en archivos que puedas encontrar con du.

En el caso habitual con contadores no negativos, GNU df calcula el porcentaje respecto a usado + disponible y lo redondea hacia arriba a un entero. Así, 60 / (60 + 35) × 100 ≈ 63,16 %, que se presenta como 64 %. No obtienes necesariamente usado / tamaño. La FAQ de GNU Coreutils explica por qué capacidad, uso y disponibilidad pueden no sumar como se espera.

La reserva exacta y la contabilidad de metadatos dependen del sistema de archivos. Además, las cantidades de -h están redondeadas: no uses 100G y 60G para reconstruir una suma byte a byte. Para una comparación precisa vuelve a -B1 y conserva el contexto del montaje.

Capacidad en bytes y capacidad en inodos

Un volumen también necesita recursos para representar objetos. En sistemas como ext4, con una cantidad de inodos definida al crear el sistema de archivos, muchos archivos pequeños pueden agotar los inodos aunque queden bloques libres. df -i cambia de magnitud y muestra esa cuenta:

terminal · dos recursos distintos
$ df -h .
$ df -i .
Filesystem      Inodes IUsed IFree IUse% Mounted on
/dev/sda2        655360 65000 590360   10% /

Es otra salida ilustrativa. Un 100 % de inodos con un 70 % de bytes usados es posible. No todos los sistemas de archivos gestionan inodos con un límite fijo o informan los mismos contadores. Aquí aprenderemos a detectar la dimensión; el tema 19 estudiará el agotamiento de recursos con más detalle.

La ruta decide qué volumen consultas

df -h /home /tmp /mnt/c puede mostrar sistemas de archivos distintos, o repetir alguno si dos rutas pertenecen al mismo. Comprueba primero que las rutas existen. En WSL, la capacidad del sistema Linux virtual, la de la unidad de Windows y el tamaño del archivo que aloja el disco virtual son niveles distintos. Liberar bloques dentro de Linux no implica que el contenedor virtual se reduzca inmediatamente en el anfitrión.

6. du: medir un recorrido de directorios

du recorre las rutas indicadas, obtiene atributos de los objetos encontrados y acumula su uso según las reglas elegidas. Por defecto se interesa por almacenamiento asignado. No pregunta cuánto queda libre y no sabe por sí solo qué archivos de otro árbol ocupan el resto del volumen.

terminal · desde un resumen hasta un primer desglose
$ cd "$LAB3"
$ du -sh .
$ du -h --max-depth=1 .
$ du -ah datos
$ du -s -B1 datos registros .cache

-s resume cada argumento; -a incluye líneas para archivos además de directorios; --max-depth=1 limita las líneas mostradas a un nivel. Esta última opción no evita recorrer los niveles profundos: hace falta contarlos para producir el total de su carpeta superior. -h vuelve legibles las cifras y -B1 las expresa en bytes.

La línea de . incluye sus descendientes y el uso atribuido al propio directorio. No sumes esa línea otra vez a las de sus subdirectorios: estarías contando parte del árbol por duplicado. La asignación de directorios también explica por qué la suma de solo los documentos puede ser algo menor que el total del recorrido.

Buscar qué rama crece

terminal · ordenar cantidades con sufijos
$ du -x -h --max-depth=1 . | sort -h

sort -h interpreta sufijos como K, M y G. Un orden alfabético no compararía correctamente 900K y 2M. La opción -x mantiene el recorrido dentro del sistema de archivos de partida y evita sumar el contenido de otros sistemas montados bajo ese árbol. No es una garantía universal contra todos los alias de montaje, como ciertos montajes bind del mismo sistema.

Esta tubería es útil para inspección humana de nombres sencillos como los del laboratorio. Si hay nombres con saltos de línea, no sirve como formato general de intercambio. Además, hay que conservar los mensajes de error: un recorrido con permisos insuficientes puede producir una suma parcial y devolver un estado distinto de cero.

El comodín y los archivos ocultos

terminal · dos conjuntos de partida distintos
$ du -sh .
$ du -sh -- *
$ du -h --max-depth=1 .

Con la configuración habitual de Bash, * no incluye las entradas ocultas del directorio actual; .cache no se entrega como argumento en la segunda orden. En cambio, du . sí la encuentra al recorrer el directorio. El carácter -- termina las opciones y permite que un argumento posterior cuyo nombre comience por guion se trate como ruta.

Hay un matiz: si * incluye un subdirectorio visible, du sí puede encontrar archivos ocultos dentro de él. La omisión del comodín afecta a los argumentos iniciales que expandió el shell; no instala una regla de «ignorar cualquier oculto en cualquier nivel».

Tamaño aparente frente a uso asignado

du --apparent-size cambia la magnitud acumulada y usa longitudes aparentes. du -b equivale en GNU a --apparent-size --block-size=1. Por tanto, du -b y du -B1 no son sinónimos:

terminal · mismo archivo, misma unidad, distinta magnitud
$ du -B1 datos/notas.txt
4096    datos/notas.txt
$ du -b datos/notas.txt
4       datos/notas.txt

En un archivo pequeño puede dominar el redondeo a unidades de asignación. En un archivo disperso puede ocurrir lo opuesto: una longitud enorme con pocos bloques. Las opciones de tamaño y enlaces están descritas en el manual de du de GNU Coreutils. A continuación mediremos ambas situaciones.

7. Experimento: longitud y bloques no coinciden

Un archivo disperso contiene regiones lógicas para las que no se han asignado bloques de datos. Leer esas regiones devuelve ceros. No hace falta almacenar un bloque lleno de ceros por cada posición lógica para representar el resultado de esas lecturas.

terminal · reservar longitud sin escribir todos sus bytes
$ cd "$LAB3"
$ truncate -s 64M datos/disperso.bin
$ ls -lh datos/disperso.bin
-rw-r--r-- 1 ana ana 64M Oct 29 10:00 datos/disperso.bin
$ stat -c 'longitud=%s bloques=%b unidad=%B' datos/disperso.bin
longitud=67108864 bloques=0 unidad=512
$ du -B1 datos/disperso.bin
0       datos/disperso.bin
$ du -b datos/disperso.bin
67108864        datos/disperso.bin

La longitud exacta es 64 × 1.048.576 = 67.108.864 bytes. El cero de bloques corresponde a un sistema que admite huecos sin asignación de datos para este caso. Otros sistemas pueden reservar o contabilizar espacio de otra manera. Aunque la cuenta sea cero, el objeto sigue necesitando inodo y entrada de directorio: cero bloques de datos no significa que crear millones de estos archivos sea gratis.

Escribir dentro de un hueco

terminal · cambiar un byte sin cambiar la longitud
$ printf 'X' | dd of=datos/disperso.bin bs=1 seek=1048576 conv=notrunc status=none
$ stat -c 'longitud=%s bloques=%b unidad=%B' datos/disperso.bin
longitud=67108864 bloques=8 unidad=512
$ od -An -tx1 -j 1048574 -N 5 datos/disperso.bin
 00 00 58 00 00
$ du -h datos/disperso.bin
4.0K    datos/disperso.bin

El byte X, cuyo valor hexadecimal es 58, ocupa la posición 1.048.576 contando desde cero. Se encuentra lejos del principio y todavía muy antes del final. conv=notrunc conserva la longitud existente. En el ejemplo se asigna un bloque de 4096 bytes; no es necesario llenar físicamente todo el tramo anterior para poder leer ceros en él.

Comparar con un archivo escrito por completo

terminal · dos archivos de igual longitud
$ dd if=/dev/zero of=datos/completo.bin bs=1M count=64 status=none
$ stat -c '%n | longitud=%s | bloques=%b | unidad=%B' datos/disperso.bin datos/completo.bin
$ du -h datos/disperso.bin datos/completo.bin
4.0K    datos/disperso.bin
64M     datos/completo.bin

En un sistema sin compresión ni optimizaciones especiales, escribir los 64 MiB asigna aproximadamente esa cantidad de datos. Los dos archivos tienen igual longitud, pero distinto contenido en la posición donde escribimos X y una asignación muy diferente. Compresión, deduplicación, preasignación y asignación diferida pueden introducir más matices; las cifras anteriores representan un caso ordinario, no una ley universal de todos los formatos.

Esta diferencia importa al copiar o respaldar. Una copia que preserve huecos puede mantener un uso bajo; otra que materialice todos los ceros puede consumir mucho más. Antes de concluir que una copia perdió datos o que un tamaño es erróneo, comprueba qué magnitud mediste y qué tratamiento reciben las regiones dispersas. En el tema 7 construiremos huecos desde C con lseek.

8. Experimento: contar enlaces y destinos

Si dos nombres se refieren al mismo inodo, sumar sus fichas como si fueran documentos independientes contaría dos veces el mismo almacenamiento. du evita normalmente esa duplicación durante una misma ejecución. Para aislar el fenómeno usaremos una carpeta nueva:

terminal · dos nombres para un MiB de datos
$ cd "$LAB3"
$ mkdir enlaces
$ dd if=/dev/zero of=enlaces/base.bin bs=1M count=1 status=none
$ ln enlaces/base.bin enlaces/otro.bin
$ stat -c '%n | dev=%d ino=%i enlaces=%h' enlaces/base.bin enlaces/otro.bin
enlaces/base.bin | dev=2049 ino=80001 enlaces=2
enlaces/otro.bin | dev=2049 ino=80001 enlaces=2
$ du -B1 -c enlaces/base.bin enlaces/otro.bin
1048576 enlaces/base.bin
1048576 total
$ du -B1 -l -c enlaces/base.bin enlaces/otro.bin
1048576 enlaces/base.bin
1048576 enlaces/otro.bin
2097152 total

Los identificadores son ilustrativos y los bytes asignados suponen la asignación ordinaria de un MiB. En la ejecución sin -l, el segundo nombre puede omitirse porque el objeto ya se contó. Con -l, abreviatura de --count-links, se cuenta por cada nombre encontrado. No se ha consumido otro MiB al cambiar una opción de observación.

La unidad de la deduplicación es la ejecución

Si ejecutas du -B1 enlaces/base.bin y luego otra orden independiente para enlaces/otro.bin, cada proceso empezará con su propia cuenta y mostrará el uso del archivo. Sumar resultados de procesos independientes puede duplicar objetos. Incluso en una sola ejecución, si los enlaces están en carpetas distintas, el orden del recorrido puede determinar a qué rama se atribuye la cuenta.

De ahí que dos informes por subdirectorios no sean siempre aditivos como si cada objeto perteneciera a una sola rama. Para una medida conjunta conviene dar las rutas a una misma ejecución, comprender la regla de enlaces y no sumar después el total general con sus subtotales.

Un enlace simbólico plantea otra pregunta

terminal · medir el enlace o seguir su destino
$ ln -s base.bin enlaces/acceso
$ stat -c '%F | longitud=%s' enlaces/acceso
symbolic link | longitud=8
$ stat -L -c '%F | longitud=%s' enlaces/acceso
regular file | longitud=1048576
$ du -B1 enlaces/acceso
$ du -B1 -L enlaces/acceso

El enlace contiene los ocho bytes de base.bin. Por defecto du mide el enlace mismo; puede no tener bloques de datos separados si se almacena dentro de sus metadatos. Con -L sigue enlaces y cuenta el destino conforme a las demás reglas. -H sigue solo los enlaces dados como argumentos iniciales, no todos los encontrados durante el recorrido.

Seguir enlaces puede llevar fuera del árbol que pensabas medir o encontrar ciclos. No añadas -L por costumbre para «hacer más completo» un informe: decide si quieres medir el objeto enlace o los destinos alcanzables. Es una decisión sobre el conjunto que estás contando.

9. Experimento: borrar sin liberar todavía

Borrar un nombre y destruir el objeto no son necesariamente el mismo instante. Si un archivo pierde su último enlace pero un proceso lo mantiene abierto, el sistema conserva el objeto para ese proceso. Sus bloques siguen ocupados y dejan de ser accesibles mediante el nombre que recorría du.

Podemos comprobarlo desde Bash sin dejar un proceso en segundo plano. Usaremos un descriptor de lectura del propio shell. El operador exec 3<... abre el archivo y deja ese descriptor abierto en la terminal; en el tema 4 estudiaremos qué significa ese número.

terminal · un objeto abierto que ya no tiene nombre
$ cd "$LAB3"
$ dd if=/dev/zero of=retenido.bin bs=1M count=8 status=none
$ exec 3<retenido.bin
$ du -B1 retenido.bin
$ df -B1 .
$ rm -- retenido.bin
$ ls -l /proc/$$/fd/3
lr-x------ 1 ana ana 64 Oct 29 10:00 /proc/1234/fd/3 -> /home/ana/tema3.A1b2C3/retenido.bin (deleted)
$ stat -L -c 'longitud=%s enlaces=%h bloques=%b unidad=%B' /proc/$$/fd/3
longitud=8388608 enlaces=0 bloques=16384 unidad=512
$ du -s -B1 .
$ df -B1 .
$ exec 3<&-
$ df -B1 .

$$ se expande al PID del shell y el número 1234 de la salida es ilustrativo. /proc/$$/fd/3 permite observar su descriptor en Linux. Después de rm, el contador de enlaces es cero, pero la ficha sigue accesible por esa referencia y su tamaño lógico es 8 × 1.048.576 = 8.388.608 bytes.

du . ya no encuentra retenido.bin porque el nombre desapareció. df puede seguir contando los bloques mientras se conserva la apertura. exec 3<&- cierra nuestro descriptor; si era la última referencia que retenía el objeto, sus recursos pueden liberarse. Si interrumpes el experimento antes de ese punto, ejecuta esa orden de cierre en la misma terminal.

No esperes ver exactamente ocho MiB de diferencia inmediata en el contador global: otros procesos escriben y liberan datos, y los sistemas pueden reflejar ciertas operaciones con demora. La evidencia principal es la combinación de nombre ausente, enlace cero y objeto todavía abierto. El experimento no requiere llenar el volumen para demostrarla.

Cómo reconocerlo en una investigación real

Si está instalada, lsof +L1 ayuda a listar archivos abiertos cuyo contador de enlaces es menor que uno. La visibilidad depende de tus permisos y no necesariamente incluye procesos de otros usuarios. Antes de actuar, identifica el proceso y su función: para un servicio, el mecanismo correcto de cierre o rotación de registros depende de ese servicio. El objetivo de este tema es observar y explicar, no terminar procesos indiscriminadamente.

En los temas 4, 5 y 25 volveremos a este caso con tablas de descriptores, close y unlink. Por ahora ya puedes explicar por qué borrar una ruta no garantiza que df disminuya en ese instante.

10. Investigar una diferencia entre df y du

Una discrepancia se investiga formulando preguntas en orden. Empezar borrando los archivos más grandes sin verificar el montaje puede no resolver el problema que observaste. El procedimiento siguiente es de lectura y reduce el conjunto de causas paso a paso.

  1. Identificar el sistema de archivos. Ejecuta df -T RUTA y, si está disponible, findmnt -T RUTA. Comprueba si estás mirando Linux, Windows, un volumen de red o un sistema virtual.
  2. Fijar la unidad y la magnitud. Compara df -B1 RUTA con du -s -x -B1 RUTA cuando te interese asignación. No sustituyas este último por du -sb sin decidir cambiar a longitudes aparentes.
  3. Comprobar el alcance. Un subdirectorio solo representa parte del volumen. Un recorrido desde la raíz puede cruzar otros montajes si no limitas el sistema de archivos.
  4. Leer los errores del recorrido. Si faltan permisos, hay rutas que desaparecen o fallos de E/S, el resultado de du puede ser parcial. Revisa su estado de salida y sus mensajes.
  5. Buscar efectos de identidad y vida útil. Considera enlaces duros, archivos borrados todavía abiertos y destinos de enlaces simbólicos que se incluyeron o excluyeron.
  6. Interpretar lo que queda fuera de un árbol de nombres. Metadatos, reservas, instantáneas y mecanismos de almacenamiento compartido pueden requerir herramientas específicas del sistema de archivos.

Una tabla de síntomas y primeras comprobaciones

ObservaciónPosible explicaciónQué mirar primero
ls muestra mucho más que du.Archivo disperso, compresión o diferencia entre longitud y asignación.stat -c '%s %b %B' archivo.
df muestra mucho más usado que du en mi carpeta.Otros directorios del mismo volumen también ocupan espacio.El montaje y el punto inicial del recorrido.
du con * omite una parte grande.El shell no incluyó entradas ocultas iniciales.du -h --max-depth=1 ..
Borré un registro y df casi no cambia.Puede seguir abierto o haber actividad simultánea.Descriptores, lsof +L1 si existe, y proceso responsable.
La suma de informes independientes supera un total conjunto.Los informes pueden contar enlaces duros más de una vez.Dispositivo, inodo y opciones de du.
Quedan bytes, pero no se crean archivos.Posible agotamiento de inodos, cuota o restricción de acceso.df -i, mensajes de error y límites aplicables.
Capacidad menos usado no coincide con disponible.Reserva y redondeo de presentación.df -B1 y contadores libres/disponibles.
du / parece incluir más de un volumen.Se cruzaron puntos de montaje.findmnt y un recorrido con -x.

Lo que no arregla cambiar de unidad

Si la diferencia se debe a un archivo abierto sin nombre, poner ambas órdenes en bytes no hará aparecer el objeto en el recorrido de du. Igualar unidades elimina una fuente de confusión, pero no iguala el alcance. Tampoco es correcto afirmar que «df siempre da más»: seguir enlaces hacia otro volumen o sumar longitudes aparentes dispersas puede producir una cifra de du mayor que el uso que estabas comparando.

Los errores de permisos merecen atención especial. Redirigir todo con 2>/dev/null hace más limpia la pantalla, pero oculta información sobre lo que no se pudo medir. Para una investigación, conserva los errores y explica las limitaciones del resultado.

En sistemas con instantáneas, reflinks, compresión o deduplicación, los bloques físicos pueden compartirse entre objetos o conservarse aunque desaparezcan de la vista actual. Sumar cuentas por archivo no siempre determina el espacio exclusivo que se recuperaría al borrarlos. Para esa pregunta se necesitan las herramientas del formato concreto; aquí aprendemos a reconocer cuándo una suma genérica no basta.

11. Un informe de diagnóstico en Bash

Reunamos las observaciones en un script que recibe un directorio y genera un informe de lectura. Usa rutas entre comillas, fija la configuración regional para que las columnas sean comparables y mantiene un estado de error si alguna medición falla. No necesita analizar la salida de ls.

Guárdalo como informe-espacio.sh en $LAB3. Las secciones muestran la ficha del directorio, los contadores del volumen y dos recorridos de du con magnitudes diferentes. Un último desglose permite decidir dónde investigar a continuación.

informe-espacio.sh · lectura y estados de salida
#!/usr/bin/env bash
set -u
set -o pipefail
export LC_ALL=C

if (( $# > 1 )); then
    printf 'Uso: %s [DIRECTORIO]\n' "$0" >&2
    exit 2
fi

ruta=${1:-.}
if [[ ! -d "$ruta" ]]; then
    printf 'No es un directorio accesible: %s\n' "$ruta" >&2
    exit 1
fi

# Desde aquí, los argumentos de las herramientas serán siempre el punto.
# cd -P sigue enlaces y pwd -P describe la ubicación física alcanzada.
if ! cd -P -- "$ruta"; then
    exit 1
fi

estado=0
printf 'DIRECTORIO: '
pwd -P || estado=1

printf '\nATRIBUTOS DEL DIRECTORIO\n'
stat -c 'tipo=%F dev=%d inodo=%i enlaces=%h modo=%a' -- . || estado=1
stat -c 'longitud=%s bloques=%b unidad=%B' -- . || estado=1

printf '\nVOLUMEN: CANTIDADES EN BYTES\n'
df -B1 --output=source,fstype,size,used,avail,pcent,target -- . || estado=1

printf '\nVOLUMEN: INODOS\n'
df -i -- . || estado=1

printf '\nRECORRIDO: ASIGNACION EN BYTES, UN SOLO SISTEMA DE ARCHIVOS\n'
du -s -x -B1 -- . || estado=1

printf '\nRECORRIDO: TAMANO APARENTE EN BYTES\n'
du -s -x --apparent-size -B1 -- . || estado=1

printf '\nDESGLOSE DE ASIGNACION: UN NIVEL, MENOR A MAYOR\n'
if ! du -x -h --max-depth=1 -- . | sort -h; then
    estado=1
fi

if (( estado != 0 )); then
    printf '\nInforme incompleto: revise los errores anteriores.\n' >&2
fi
exit "$estado"
terminal · ejecutar y comprobar el resultado
$ cd "$LAB3"
$ bash -n informe-espacio.sh
$ bash informe-espacio.sh "$LAB3"
$ echo $?
0
$ bash informe-espacio.sh "$LAB3/no-existe"
No es un directorio accesible: /home/ana/tema3.A1b2C3/no-existe
$ echo $?
1

Por qué están esas comprobaciones

El desglose final está pensado para lectura humana, como la tubería anterior. El script no ofrece una instantánea atómica: entre una consulta y otra, otros procesos pueden cambiar archivos, enlaces o montajes. Además, recorrer dos veces un árbol grande puede ser costoso. Ejecútalo sobre el directorio que necesitas investigar, sin convertirlo en un barrido permanente de todo el equipo.

Si quieres guardar el resultado, coloca el informe fuera del árbol medido para no introducir en el propio recorrido un archivo que va creciendo mientras se escribe. Por ejemplo, desde el laboratorio: bash informe-espacio.sh "$LAB3" > "$HOME/informe-tema3.txt". Si también quieres conservar errores, puedes usar un archivo separado para stderr.

12. Simulador: seguir la cuenta del espacio

El simulador representa un volumen de 128 MiB. Dentro de la carpeta estudiada hay un archivo normal de 16 MiB con un segundo enlace duro, un archivo disperso de longitud 64 MiB que tiene 4 MiB asignados y un subdirectorio oculto de tamaño variable. Fuera de esa carpeta puede haber otros archivos y objetos borrados aún abiertos.

Todos los importes del modelo son múltiplos enteros de MiB, sin compresión ni bloques compartidos entre archivos diferentes. Separamos 4 MiB de metadatos globales que no se atribuyen al recorrido de esta carpeta. Para simplificar, omitimos del total de du la asignación propia de los directorios. La reserva representa bloques todavía libres que no se ofrecen como disponibles ordinarios.

Mismo volumen, mediciones diferentes

MediciónResultado

Qué predicciones comprobar

  1. Con los valores iniciales, el uso del volumen es 16 + 4 + 8 + 32 + 12 + 4 = 76 MiB. Los libres son 128 − 76 = 52 MiB; al excluir la reserva de 5 quedan 47 MiB disponibles. El porcentaje resulta del cociente 76 / (76 + 47), redondeado hacia arriba: 62 %.
  2. El recorrido inicial incluye 16 + 4 + 8 = 28 MiB asignados. Cambiar a tamaño aparente da 16 + 64 + 8 = 88 MiB. No ha cambiado la asignación del volumen.
  3. Activa --count-links: el segundo nombre del archivo normal añade otros 16 MiB a la cuenta de du. Tampoco crea datos nuevos en el volumen.
  4. Reduce los borrados abiertos de 12 a cero. La cuenta de du permanece igual y el usado de df baja. El control representa cerrar la última referencia de esos objetos.
  5. Excluye el subdirectorio oculto del recorrido. Cambia lo que cuenta du; los bytes siguen ocupados en el volumen. Es una omisión del conjunto de partida, como la que puede producir un comodín.

Si introduces cantidades que exceden la capacidad del modelo, se indicará el error. No se inventa una disponibilidad negativa ni se alteran silenciosamente los valores. El simulador es didáctico: no lee tus discos y no reproduce los detalles de ningún formato concreto.

13. Resumen y guía rápida

Necesito saber…Punto de partida
Qué nombres hay, incluidos los ocultos.ls -lA directorio
La ficha de la carpeta, sin listar su contenido.ls -ld directorio
Longitud y asignación de un objeto.stat -c '%s %b %B' archivo
Qué espacio ofrece el volumen de una ruta.df -h ruta
Si faltan inodos.df -i ruta
Asignación acumulada de una carpeta.du -s -x -B1 directorio
Tamaño aparente del recorrido.du -s -x --apparent-size -B1 directorio
Qué rama merece investigarse.du -x -h --max-depth=1 directorio | sort -h

14. Preguntas de repaso y práctica

Responde primero sin ejecutar comandos. Después contrasta tu razonamiento con los experimentos y despliega las soluciones.

1. df archivo muestra 60 GiB usados y el archivo mide cuatro bytes. ¿Es un error?

No. df informa del sistema de archivos que contiene esa ruta, no del tamaño del documento. Los otros objetos y la contabilidad del volumen forman parte de su alcance.

2. ¿Qué diferencia hay entre ls -l datos y ls -ld datos?

La primera orden lista normalmente el contenido de datos. La segunda muestra la ficha del propio directorio. Su longitud no es la suma recursiva de los archivos que contiene.

3. stat muestra tamaño 5000, bloques 16 y unidad 512. ¿Qué informa du -B1 en el caso ordinario?

8192 bytes asignados: 16 × 512. La longitud lógica sigue siendo 5000 bytes. El ejemplo corresponde a un objeto que necesita dos bloques de asignación de 4096 bytes.

4. ¿du -b y du -B1 miden lo mismo?

No. Ambas presentan bytes, pero -b equivale a tamaño aparente con unidad de un byte; -B1 mantiene la magnitud de uso asignado por defecto y cambia su unidad de presentación.

5. Un archivo disperso mide 64 MiB y tiene un solo bloque de 4 KiB. ¿Cuántos bytes cuenta cada medida?

La longitud es 67.108.864 bytes; la asignación del ejemplo es 4096 bytes, que stat expresa como ocho unidades de 512. du -b muestra la primera y du -B1 la segunda.

6. ¿Por qué du --max-depth=1 puede tardar mucho en un árbol profundo?

La opción limita las líneas mostradas, no el trabajo necesario para sumar sus descendientes. El programa sigue teniendo que medir las ramas profundas para calcular el total de cada directorio superior.

7. ¿Por qué du -sh * puede omitir .cache, pero du -sh . la incluye?

El comodín lo expande Bash y normalmente no incluye los nombres iniciales que comienzan por punto. Al recibir el directorio punto, du hace su propio recorrido y encuentra esas entradas. No se trata de que du ignore por defecto los ocultos.

8. Dos enlaces duros a un archivo de un MiB, medidos juntos, no suman dos MiB. ¿Por qué?

Porque representan el mismo objeto y du evita contarlo de nuevo durante la misma ejecución. Con --count-links se puede pedir una cuenta por cada nombre encontrado, que no equivale a ocupar dos veces el almacenamiento.

9. ¿Qué cambia al añadir -L a du?

Se siguen los enlaces simbólicos y se consideran los destinos alcanzados según las demás reglas. Sin esa opción se cuenta el enlace mismo. El conjunto medido puede extenderse fuera de la carpeta inicial.

10. Borras el último nombre de un archivo, pero el contador del volumen no baja. ¿Qué comprobarías?

Si algún proceso conserva el objeto abierto. Un contador de enlaces cero no elimina los bloques mientras sigan retenidos por referencias de ese tipo. También hay que considerar actividad concurrente, instantáneas y las particularidades del sistema.

11. ¿Puede un volumen sin espacio disponible tener todavía bloques libres?

Sí. Puede conservar bloques que no estén disponibles para asignaciones ordinarias, por ejemplo una reserva. Libre y disponible son contadores relacionados pero no necesariamente iguales.

12. Con 60 GiB usados y 35 GiB disponibles, ¿por qué df puede mostrar 64 %?

El cociente habitual es 60 / (60 + 35), aproximadamente 63,16 %, y GNU df lo redondea hacia arriba al entero 64. Dividir por una capacidad de 100 GiB respondería a otra fórmula.

13. ¿Para qué sirve df -i si df -h aún muestra espacio?

Para consultar la capacidad en inodos. En sistemas con una tabla limitada, se pueden agotar los objetos representables antes que los bloques de datos. Una cuota u otros límites también pueden impedir crear archivos; hay que leer el error concreto.

14. ¿Por qué el script activa pipefail antes de du | sort?

Para que un fallo de du no quede oculto por el éxito de sort. Sin pipefail, el estado de la tubería se toma normalmente del último proceso; un ordenamiento correcto no demuestra que el recorrido haya sido completo.

15. ¿La suma de stat por todos los nombres garantiza el espacio que se liberaría al borrarlos?

No. Puede duplicar enlaces duros, omitir objetos sin nombre y no representar espacio exclusivo en sistemas con bloques compartidos o instantáneas. Tampoco incluye necesariamente toda la contabilidad global del sistema de archivos.

16. ¿Una medición sin errores es una instantánea exacta de un árbol activo?

No necesariamente. El recorrido y las consultas ocurren en momentos distintos. Los archivos pueden crecer o desaparecer mientras se miden; por eso hay que conservar el contexto temporal y no atribuir precisión atómica a un conjunto de órdenes.

Para practicar

  1. Completa el laboratorio y crea una tabla con longitud, bloques asignados y bytes asignados para notas.txt, mediano.bin, vacio y app.log. Comprueba cada asignación con du -B1.
  2. Compara ls -lh, ls -l --si y stat -c %s sobre el archivo de dos MiB. Explica la unidad y el redondeo de cada presentación.
  3. Ejecuta ls -ld datos y du -s -B1 datos. Añade un archivo de un MiB y repite. Explica por qué la ficha del directorio no aumenta necesariamente un MiB.
  4. Haz el experimento disperso y escribe otro byte cerca del final, con seek=67108863. Compara la longitud y la asignación antes y después. El número de bloques que aumente dependerá de la política de asignación.
  5. Crea un tercer enlace duro a enlaces/base.bin. Compara una ejecución conjunta de du, tres ejecuciones independientes y una con -l. Distingue la suma de nombres de la de objetos.
  6. Consulta df -T y findmnt -T para el laboratorio y para /proc. Explica por qué no deberías sumar el espacio de ambos como si fueran carpetas ordinarias del mismo volumen.
  7. Repite el experimento del archivo abierto y borrado. Anota el contador de enlaces y cierra el descriptor 3 al terminar. Usa la referencia de proc para distinguir ausencia de nombre y desaparición del objeto.
  8. Ejecuta el informe sobre un directorio cuyo nombre contenga espacios y después sobre una ruta inexistente. Comprueba el resultado con echo $?. Explica por qué las comillas y el estado de salida forman parte del informe.
  9. Modifica el script para mostrar también stat -f con la unidad fundamental, los bloques libres y los disponibles. No reutilices los significados de los formatos de stat sin -f.
  10. En el simulador consigue que el tamaño aparente de du supere los bytes usados de df sin exceder la capacidad del volumen. Después consigue una diferencia grande en sentido contrario. Explica qué controles intervienen en cada caso.

Con este tema termina el primer bloque del curso. Ya puedes observar nombres, atributos, tamaños, espacio e inodos sin confundir sus alcances. En el tema 4 entraremos en los procesos: qué es un descriptor de archivo, por qué la entrada, salida y error estándar son 0, 1 y 2, y qué tablas mantiene el núcleo cuando abrimos un archivo.