cp es la herramienta que usa el kernel para duplicar datos en el sistema de archivos: lee los bloques del origen y los escribe en una nueva ubicación. Lo importante es entender que, por defecto, cp no copia metadatos: crea un archivo nuevo con tu usuario como propietario, con la fecha y hora actuales, y sin preservar permisos especiales. El archivo resultante es funcionalmente idéntico en contenido, pero no es una copia fiel del original.
Esto tiene sentido como comportamiento predeterminado cuando estás copiando archivos para trabajar con ellos —quieres que el archivo nuevo sea tuyo, modificable, con timestamps frescos—. Pero cuando necesitas una copia fiel para un backup o para mover una instalación a otro disco, ese comportamiento por defecto te traiciona silenciosamente.
Úsalo para duplicar archivos antes de editarlos, para preparar backups, para poblar directorios de configuración, o para mover contenido entre sistemas de archivos cuando mv no es opción (por ejemplo, entre particiones distintas). Lo que rompes si lo usas mal: sobrescribir un archivo sin querer porque no pusiste -i, perder permisos de un ejecutable o un archivo setuid, o copiar un directorio con -r pensando que tienes una copia fiel cuando en realidad los symlinks se han convertido en copias del contenido al que apuntaban.
El comportamiento del destino es la trampa más común: si el destino es un directorio existente, cp coloca el archivo dentro de ese directorio. Si el destino es un nombre que no existe, crea un archivo con ese nombre. Si el destino es un nombre que ya existe como archivo, lo sobreescribe sin preguntar (salvo que uses -i).
# Estructura de ejemplo: tenemos un proyecto web con configuración y assets
mkdir -p ~/proyecto/{config,assets,logs}
echo "db_host=localhost" > ~/proyecto/config/database.conf
echo "debug=true" >> ~/proyecto/config/database.conf
cp /etc/hosts ~/proyecto/config/hosts.copia
touch ~/proyecto/assets/logo.png ~/proyecto/assets/banner.png
ln -s ../config/database.conf ~/proyecto/assets/db_link # symlink de prueba
# 1. Copia simple: el destino no existe → crea el archivo con ese nombre
cp ~/proyecto/config/database.conf ~/proyecto/config/database.conf.bak
# 2. -i: pregunta antes de sobreescribir; imprescindible en operaciones manuales
cp -i ~/proyecto/config/database.conf ~/proyecto/config/database.conf.bak
# 3. Copia a directorio existente: el archivo va dentro del directorio
cp ~/proyecto/config/database.conf ~/proyecto/logs/
# Resultado: ~/proyecto/logs/database.conf
# 4. Copiar varios archivos a un directorio (el destino debe ser directorio)
cp ~/proyecto/assets/logo.png ~/proyecto/assets/banner.png ~/proyecto/logs/
# 5. -p: preserva permisos, timestamps y owner — verifica la diferencia
chmod 640 ~/proyecto/config/database.conf
cp ~/proyecto/config/database.conf /tmp/sin_preservar.conf
cp -p ~/proyecto/config/database.conf /tmp/con_preservar.conf
stat -c "%a %U %y %n" \
~/proyecto/config/database.conf \
/tmp/sin_preservar.conf \
/tmp/con_preservar.conf
# 6. -r: copia recursiva de directorio — copia contenido pero NO preserva metadatos
cp -r ~/proyecto/config /tmp/config_sin_meta
# 7. -a (archive): equivale a -r + -p + preservación de symlinks
# Úsalo cuando quieras una copia que sea indistinguible del original
cp -a ~/proyecto /tmp/proyecto_backup
# Comprueba que el symlink sigue siendo symlink (no una copia del contenido)
ls -la /tmp/proyecto_backup/assets/db_link
# → debe mostrar "db_link -> ../config/database.conf", no un archivo regular
# Comprueba timestamps y permisos del directorio backup vs original
stat -c "%a %U %y %n" ~/proyecto/config/database.conf \
/tmp/proyecto_backup/config/database.conf
El bloque de stat en el paso 5 es el que más enseña: ejecuta esa comparación y verás que /tmp/sin_preservar.conf tiene timestamp de ahora mismo y permisos 644 (la umask por defecto aplicada sobre el archivo nuevo), mientras que /tmp/con_preservar.conf replica exactamente los 640 y la fecha de modificación del original. Eso es lo que -p compra.
En el paso 6, cp -r copia el árbol completo, pero cada archivo nuevo hereda tu umask y timestamp actual. Si ese directorio contenía ejecutables con permisos específicos o archivos de configuración con 640, los pierdes. Es por eso que -r solo es adecuado cuando necesitas el contenido, no la fidelidad.
El paso 7 con -a es el que deberías usar para backups. La bandera -a equivale internamente a -dR --preserve=all: -d mantiene los symlinks como symlinks en lugar de seguirlos y copiar el destino, -R es la recursividad, y --preserve=all cubre permisos, timestamps, owner, grupo, ACLs y atributos extendidos. El ls -la del symlink db_link te confirma visualmente que sigue siendo un enlace simbólico y no un archivo regular —ese detalle marca la diferencia entre -r y -a.
El comportamiento del destino en el paso 7 merece atención: como /tmp/proyecto_backup no existía, cp -a crea ese directorio con el contenido de ~/proyecto dentro. Si /tmp/proyecto_backup ya existiera como directorio, cp -a ~/proyecto /tmp/proyecto_backup/ pondría el directorio proyecto dentro, resultando en /tmp/proyecto_backup/proyecto/. Esa distinción —barra final, existencia previa del destino— es la fuente del 80% de los errores con cp en producción.
N° 20