- Команда cp: правильное копирование папок с файлами в *nix
- Выводы
- Послесловие
- How do I create a copy of a directory in Unix/Linux? [closed]
- 3 Answers 3
- Linux fundamentals: How to copy, move, and rename files and directories
- Move files and directories
- Rename files and directories
- Copy files and directories
- Great Linux resources
- More to explore
Команда cp: правильное копирование папок с файлами в *nix
В этой статье будут раскрыты некоторые неочевидные вещи связанные с использованием wildcards при копировании, неоднозначное поведение команды cp при копировании, а также способы позволяющие корректно копировать огромное количество файлов без пропусков и вылетов.
Допустим нам нужно скопировать всё из папки /source в папку /target.
Первое, что приходит на ум это:
Сразу исправим эту команду на:
Ключ -a добавит копирование всех аттрибутов, прав и добавит рекурсию. Когда не требуется точное воспроизведение прав достаточно ключа -r .
После копирования мы обнаружим, что скопировались не все файлы — были проигнорированы файлы начинающиеся с точки типа:
.profile
.local
.mc
и тому подобные.
Потому что wildcards обрабатывает shell ( bash в типовом случае). По умолчанию bash проигнорирует все файлы начинающиеся с точек, так как трактует их как скрытые. Чтобы избежать такого поведения нам придётся изменить поведение bash с помощью команды:
Чтобы это изменение поведения сохранилось после перезагрузки, можно сделать файл wildcard.sh c этой командой в папке /etc/profile.d (возможно в вашем дистрибутиве иная папка).
А если в директории-источнике нет файлов, то shell не сможет ничего подставить вместо звёздочки, и также копирование завершится с ошибкой. Против подобной ситуации есть опции failglob и nullglob . Нам потребуется выставить failglob , которая не даст команде выполниться. nullglob не подойдёт, так как она строку с wildcards не нашедшими совпадения преобразует в пустую строку (нулевой длины), что для cp вызовет ошибку.
Однако, если в папке тысячи файлов и больше, то от подхода с использованием wildcards стоит отказаться вовсе. Дело в том, что bash разворачивает wildcards в очень длинную командную строку наподобие:
cp -a /souce/a /source/b /source/c …… /target
На длину командной строки есть ограничение, которое мы можем узнать используя команду:
Получим максимальную длину командной строки в байтах:
…. Maximum length of command we could actually use: 2089314 ….
Итак, давайте будем обходиться вовсе без wildcards.
И тут мы столкнёмся с неоднозначностью поведения cp . Если папки /target не существует, то мы получим то, что нам нужно.
Однако, если папка target существует, то файлы будут скопированы в папку /target/source.
Не всегда мы можем удалить заранее папку /target, так как в ней могут быть нужные нам файлы и наша цель, допустим, дополнить файлы в /target файлами из /source.
Если бы папки источника и приёмника назывались одинаково, например, мы копировали бы из /source в /home/source, то можно было бы использовать команду:
И после копирования файлы в /home/source оказались бы дополненными файлами из /source.
Такая вот логическая задачка: мы можем дополнить файлы в директории-приёмнике, если папки называются одинаково, но если они отличаются, то папка-исходник будет помещена внутрь приёмника. Как скопировать файлы из /source в /target с помощью cp без wildcards?
Чтобы обойти это вредное ограничение мы используем неочевидное решение:
Те кто хорошо знаком с DOS и Linux уже всё поняли: внутри каждой папки есть 2 невидимые папки «.» и «..», являющиеся псевдопапками-ссылками на текущую и вышестоящие директории.
- При копировании cp проверяет существование и пытается создать /target/.
- Такая директория существует и это есть /target
- Файлы из /source скопированы в /target корректно.
Поведение этой команды однозначно. Всё отработает без ошибок вне зависимости от того миллион у вас файлов или их нет вовсе.
Выводы
Если нужно скопировать все файлы из одной папки в другую, не используем wildcards, вместо них лучше использовать cp в сочетании с точкой в конце папки-источника. Это скопирует все файлы, включая скрытые и не завалится при миллионах файлов или полном отсутствии файлов.
Послесловие
vmspike предложил аналогичный по результату вариант команды:
ВНИМАНИЕ: регистр буквы T имеет значение. Если перепутать, то получите полную белиберду: направление копирования поменяется.
Благодарности:
- Компании RUVDS.COM за поддержку и возможность публикации в своем блоге на Хабре.
- За изображение TripletConcept. Картинка очень большая и детальная, можно открыть в отдельном окне.
How do I create a copy of a directory in Unix/Linux? [closed]
I want to recursively create a copy of a directory and all its contents (e.g. files and subdirectories).
3 Answers 3
The option you’re looking for is -R .
cp -R path_to_source path_to_destination/
- If destination doesn’t exist, it will be created.
- -R means copy directories recursively . You can also use -r since it’s case-insensitive.
- To copy everything inside the source folder (symlinks, hidden files) without copying the source folder itself use -a flag along with trailing /. in the source (as per @muni764 ‘s / @Anton Krug ‘s comment):
cp -a path_to_source/. path_to_destination/
i wonder why this exact command in dockerfile copies all source directory files into destination, instead of copying just whole directory.
I believe the ‘/’ on the end makes a difference and that might account for your experience. If the source includes the trailing slash it will copy what is in the directory only. If it does not include the trailing slash, it will copy the directory as well and then the contents inside of it. My memory is this behavior varies by command and maybe event by OS a bit. Here’s a reference with more info.
I would say if you don’t want to include the source and you want to make sure everything is copied (symlinks, hidden files) without copying the source parent folder is to use -ra source/. destination. This will make sure the content of the folder is copied, but not the parent folder itself, which is sometimes handy. And the difference is the /.
Note the importance of «Slash dot» on your source in cp -r src/. dest I know it is mentioned but I still seem to miss it every time.
You are looking for the cp command. You need to change directories so that you are outside of the directory you are trying to copy.
If the directory you’re copying is called dir1 and you want to copy it to your /home/Pictures folder:
Linux is case-sensitive and also needs the / after each directory to know that it isn’t a file. ~ is a special character in the terminal that automatically evaluates to the current user’s home directory. If you need to know what directory you are in, use the command pwd .
When you don’t know how to use a Linux command, there is a manual page that you can refer to by typing:
Also, to auto complete long file paths when typing in the terminal, you can hit Tab after you’ve started typing the path and you will either be presented with choices, or it will insert the remaining part of the path.
There is an important distinction between Linux and Unix in the answer because for Linux (GNU and BusyBox) -R , -r , and —recursive are all equivalent, as mentioned in this answer. For portability, i.e. POSIX compliance, you would want to use -R because of some implementation-dependent differences with -r . It’s important to read the man pages to know any idiosyncrasies that may arise (this is a good use case to show why POSIX standards are useful).
Linux fundamentals: How to copy, move, and rename files and directories
Copying, moving, and renaming files and directories are standard tasks for sysadmins and end users. Depending on your Linux distribution, you can accomplish these operations in various ways.
The Bash shell is usually the most efficient tool for file management. This article assumes you already have a basic understanding of how to open a Linux terminal and enter commands. (See How to access the Linux terminal if you want a refresher.) Connect to your Linux terminal with your regular user account, and get ready to reorganize.
Change to your home directory and create a new directory named mydir for the exercises. The command to create a new directory is mkdir :
Move files and directories
The mv command moves both directories and files. Check its options and parameters from the —help results below:
$ mv --help Usage: mv [OPTION]. [-T] SOURCE DEST or: mv [OPTION]. SOURCE. DIRECTORY or: mv [OPTION]. -t DIRECTORY SOURCE. Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY. Mandatory arguments to long options are mandatory for short options too. --backup[=CONTROL] make a backup of each existing destination file -b like --backup but does not accept an argument -f, --force do not prompt before overwriting -i, --interactive prompt before overwrite -n, --no-clobber do not overwrite an existing file If you specify more than one of -i, -f, -n, only the final one takes effect. --strip-trailing-slashes remove any trailing slashes from each SOURCE argument -S, --suffix=SUFFIX override the usual backup suffix -t, --target-directory=DIRECTORY move all SOURCE arguments into DIRECTORY -T, --no-target-directory treat DEST as a normal file -u, --update move only when the SOURCE file is newer than the destination file or when the destination file is missing -v, --verbose explain what is being done -Z, --context set SELinux security context of destination file to default type --help display this help and exit --version output version information and exit The backup suffix is '~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX. The version control method may be selected via the --backup option or through the VERSION_CONTROL environment variable. Here are the values: none, off never make backups (even if --backup is given) numbered, t make numbered backups existing, nil numbered if numbered backups exist, simple otherwise simple, never always make simple backups
Rename files and directories
You also use the mv command to rename directories and files if the destination doesn’t already exist. If the destination exists, then they’re moved using the syntax mv . Here is an example of moving existing files to existing directories:
$ ls -l total 0 drwxrwxr-x. 2 localuser localuser 6 Jun 9 14:57 dir1 drwxrwxr-x. 2 localuser localuser 6 Jun 9 17:52 dir2 drwxrwxr-x. 2 localuser localuser 6 Jun 9 17:52 dir3 drwxrwxr-x. 3 localuser localuser 21 Jun 9 16:57 dir4 -rw-rw-r--. 1 localuser localuser 0 Jun 9 17:31 file1 -rw-rw-r--. 1 localuser localuser 0 Jun 9 17:33 file2 -rw-rw-r--. 1 localuser localuser 0 Jun 9 17:33 file3 $ mv file1 dir1/ $ mv file2 dir2/ $ mv file3 dir3/ $ ls -l total 0 drwxrwxr-x. 2 localuser localuser 19 Jun 9 17:53 dir1 drwxrwxr-x. 2 localuser localuser 19 Jun 9 17:53 dir2 drwxrwxr-x. 2 localuser localuser 19 Jun 9 17:53 dir3 drwxrwxr-x. 3 localuser localuser 21 Jun 9 16:57 dir4 [mydir]$ ls -lR .: total 0 drwxrwxr-x. 2 localuser localuser 19 Jun 9 17:53 dir1 drwxrwxr-x. 2 localuser localuser 19 Jun 9 17:53 dir2 drwxrwxr-x. 2 localuser localuser 19 Jun 9 17:53 dir3 drwxrwxr-x. 3 localuser localuser 21 Jun 9 16:57 dir4 ./dir1: total 0 -rw-rw-r--. 1 localuser localuser 0 Jun 9 17:31 file1 ./dir2: total 0 -rw-rw-r--. 1 localuser localuser 0 Jun 9 17:33 file2 ./dir3: total 0 -rw-rw-r--. 1 localuser localuser 0 Jun 9 17:33 file3 ./dir4: total 0 drwxrwxr-x. 2 localuser localuser 19 Jun 9 17:35 subdir1 ./dir4/subdir1: total 0 -rw-rw-r--. 1 localuser localuser 0 Jun 9 17:35 file4
[ Boost your Bash skills. Download the Bash shell scripting cheat sheet. ]
And you can use the mv command to move directories into other directories:
$ ls -1 dir1 dir2 dir3 dir4 $ mv dir1/ dir2/ $ mv dir2/ dir3/ $ ls -1 dir3 dir4
The dir1 and dir2 directories still exist; you’ve just moved them. See what it looks like for yourself:
$ ls -R dir3 dir4 ./dir3: dir2 file3 ./dir3/dir2: dir1 file2 ./dir3/dir2/dir1: file1 ./dir4: subdir1 ./dir4/subdir1: file4
Copy files and directories
The cp command copies both files and directories. This command has many options, but the basic syntax is simple. Run cp to copy from one place (source) to another (destination). Consider the following example:
$ ls -1 dir3 dir4 $ cp dir4/subdir1/file4 . $ ls -1 dir3 dir4 file4 $ ls -R dir4/ dir4/: subdir1 dir4/subdir1: file4
To copy an entire directory with its contents, use the -R option, as seen below:
$ cp -R dir3/ dir4/ $ ls -R dir3 dir4 file4 ./dir3: total 0 dir2 file3 ./dir3/dir2: dir1 file2 ./dir3/dir2/dir1: file1 ./dir4: dir3 subdir1 ./dir4/dir3: dir2 file3 ./dir4/dir3/dir2: file2 ./dir4/dir3/dir2/dir1: file1 ./dir4/subdir1: file4
Great Linux resources
When you copy empty directories into other directories, there’s no need for the -R parameter.
More to explore
For each command I’ve demonstrated, there are many more options I’ve left out for the sake of brevity.
As a sysadmin, you must know how to copy, move, and rename files and directories. These file-management commands are the basis of much of what you do on the system and are the building blocks for effective Linux administration. I hope this article aids you in understanding this topic, helps your Linux certification path, and adds to your general sysadmin knowledge.