cp знают все. Но используют в основном один вариант — и каждый раз гуглят когда нужно что-то нестандартное. Скопировать папку сохранив симлинки, показать прогресс копирования большого файла, не затереть более новую версию — всё это cp умеет, просто нужно знать правильные флаги.
Основы: файл, папка, маска
Скопировать файл в директорию:
cp file.txt /path/to/folder/
Скопировать и сразу переименовать:
cp file.txt /path/to/folder/newname.txt
Несколько файлов за один вызов:
cp file1.txt file2.txt file3.txt /path/to/folder/
По маске — все .log файлы в текущей директории:
cp *.log /var/backup/logs/
Директории: -r и ловушка с существующей целью
Без флага -r попытка скопировать папку даст omitting directory. Рекурсивное копирование:
cp -r /var/www/mysite /var/backup/mysite
Здесь есть нюанс который часто удивляет. Если /var/backup/mysite уже существует — папка скопируется внутрь и получится /var/backup/mysite/mysite. Если не существует — создастся с нужным именем. Это поведение не меняется флагами, нужно учитывать при написании скриптов.
Сохранить метаданные: -p и -a
Обычный cp создаёт новый файл с текущей датой и правами из umask. Для резервных копий это проблема — теряются оригинальные временны́е метки и владелец.
Флаг -p сохраняет: режим доступа, владельца, группу, временны́е метки:
cp -rp /etc/nginx /backup/nginx
Флаг -a (archive) — ещё полнее: включает -r, -p, и дополнительно сохраняет символические ссылки как ссылки, а не разворачивает их:
cp -a /var/www/mysite /backup/mysite
Для большинства сценариев резервного копирования -a предпочтительнее -rp.
Не затирать и спрашивать: -n, -i, -u
Три флага с похожим назначением, но разным поведением.
-n (no-clobber) — молча пропустить если файл уже есть:
cp -n source.txt /target/
-i (interactive) — спросить перед заменой:
cp -i file.txt /target/
-u (update) — скопировать только если источник новее чем существующий файл в цели, или если в цели его вообще нет:
cp -u /var/www/html/*.php /backup/html/
-u — это грубый инструмент синхронизации. Он не удаляет файлы которые исчезли из источника — только добавляет и обновляет.
Прогресс-бар для больших файлов
cp не показывает прогресс — просто молчит до конца. Для копирования файлов в несколько гигабайт это неудобно. Решение — пайп через pv (pipe viewer):
Установить pv если не установлен:
sudo apt-get install pv
Скопировать с прогресс-баром:
pv /source/large_file.iso > /target/large_file.iso
Для директорий — через tar:
tar cf - /source/folder | pv | tar xf - -C /target/
Вывод покажет скорость, объём переданного и примерное время до конца.
Файлы с пробелами в имени
Имя файла с пробелами без кавычек сломает команду — bash разобьёт его на несколько аргументов.
Использовать кавычки:
cp "my file with spaces.txt" /target/
Или экранировать пробелы обратным слешем:
cp my\ file\ with\ spaces.txt /target/
При копировании через маску файлов с пробелами лучше использовать find с -exec:
find /source -name "*.txt" -exec cp {} /target/ \;
Мгновенное копирование на btrfs и xfs: --reflink
На файловых системах btrfs и xfs есть механизм copy-on-write. Флаг --reflink=auto создаёт «виртуальную» копию мгновенно, без реального дублирования данных — место на диске тратится только когда файл начинают изменять:
cp --reflink=auto large_database.sql /backup/large_database.sql
Копирование файла в 10 ГБ занимает миллисекунды. На ext4 флаг игнорируется и cp работает обычным образом — auto означает «использовать если поддерживается».
Ротация бэкапов с датой
Копировать конфиг с датой в имени перед каждой правкой — хорошая привычка:
cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.$(date +%Y%m%d_%H%M%S)
Получится: nginx.conf.20260305_142233. Можно видеть когда был сделан каждый бэкап.
Автоматически удалять копии старше 30 дней:
find /etc/nginx -name "nginx.conf.*" -mtime +30 -delete
Копирование только определённых типов файлов рекурсивно
cp не умеет фильтровать по расширению рекурсивно — только *.ext в текущей директории. Для рекурсивной фильтрации нужен find:
Скопировать все .php файлы сохраняя структуру директорий:
find /var/www/html -name "*.php" -exec cp --parents {} /backup/ \;
--parents воспроизводит путь к файлу в целевой директории.
Когда cp недостаточно: rsync
Для регулярной синхронизации cp проигрывает rsync. Тот умеет копировать только изменившиеся блоки, работать по SSH, исключать по маске и удалять из цели то чего уже нет в источнике.
Синхронизировать директорию:
rsync -av --delete /var/www/html/ /backup/html/
Скопировать на удалённый сервер:
rsync -avz /var/www/html/ user@192.168.0.10:/backup/html/
Слеш после источника важен: с ним копируется содержимое, без него — сама папка внутрь целевой.
Шпаргалка
| Задача | Команда |
|---|---|
| Скопировать файл | cp file.txt /target/ |
| Скопировать и переименовать | cp file.txt /target/newname.txt |
| Скопировать директорию (все метаданные) | cp -a /source/ /target/ |
| Не перезаписывать существующее | cp -n file.txt /target/ |
| Спрашивать перед заменой | cp -i file.txt /target/ |
| Только новые/изменённые | cp -u /source/*.php /target/ |
| С прогресс-баром | pv source.iso > /target/source.iso |
| Файл с пробелами в имени | cp "my file.txt" /target/ |
| Мгновенно на btrfs/xfs | cp --reflink=auto file /target/ |
| Бэкап с датой | cp file.conf file.conf.$(date +%Y%m%d) |
| Рекурсивно по расширению | find /src -name "*.php" -exec cp --parents {} /dst/ \; |
| С выводом прогресса | cp -rv /source/ /target/ |