Copying folder and files in linux

Команда 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

На длину командной строки есть ограничение, которое мы можем узнать используя команду:

Читайте также:  Make error 127 linux

Получим максимальную длину командной строки в байтах:

…. 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. Картинка очень большая и детальная, можно открыть в отдельном окне.

Источник

Copy a Directory in Linux – How to cp a Folder in the Command Line in Linux and Unix (MacOS)

Copy a Directory in Linux – How to cp a Folder in the Command Line in Linux and Unix (MacOS)

To copy files or directories in Unix-based operating systems (Linux and MacOS), you use the cp command.

Читайте также:  Auto mount hdd linux

The cp command is a relatively simple command, but its behavior changes slightly depending on the inputs (files vs directories) and the options you pass to it.

To view the documentation or manual for the cp command, run man cp at your terminal:

$ man cp NAME cp -- copy files SYNOPSIS cp [OPTIONS] source_file target_file cp [OPTIONS] source_file . target_directory . 

The basic form of this command takes an input source (or sources) that you want to copy (files or directories) and a destination to copy the files or directories to:

cp [OPTIONS] source_file target_file

How to copy a file to the current directory

To copy a file, pass the file you want to copy and the path of where you want to copy the file to.

If you have a file named a.txt , and you want a copy of that file named b.txt :

$ ls a.txt $ cp a.txt b.txt $ ls a.txt b.txt

By default the cp command uses your current directory as the path.

How to copy a file to another directory

To copy a file to a directory that is different from your current directory, you just need to pass the path of the other directory as the destination:

$ ls ../directory-1/ $ cp a.txt ../directory-1/ $ ls ../directory-1/ a.txt 

After the cp command, the previously empty directory-1 now contains the file a.txt .

By default the copied file receives the name of the original file, but you can also optionally pass a file name as well:

$ cp a.txt ../directory-1/b.txt $ ls ../directory-1/ b.txt

How to copy multiple files to a directory

To copy more than one file at a time you can pass multiple input sources and a directory as destination:

$ ls ../directory-1/ $ cp first.txt second.txt ../directory-1/ $ ls ../directory-1/ first.txt second.txt 

Here the two input sources ( first.txt and second.txt ) were both copied to the directory directory-1 .

How to copy a directory to another directory

If you try to pass a directory as the input source, you get this error:

$ cp directory-1 directory-2 cp: directory-1 is a directory (not copied).

To copy a directory, you need to add the -r (or -R ) flag—which is shorthand for —recursive :

$ ls directory-1 a.txt $ cp -r directory-1 directory-2 $ ls directory-1 directory-2 $ ls directory-2 a.txt

Here directory-1 containing the file a.txt is copied to a new directory called directory-2 —which now also contains the file a.txt .

Читайте также:  Kali linux on android root

How to copy the entire directory vs the contents of the directory

There is an interesting edge case when you copy a directory: if the destination directory already exists, you can choose whether to copy the contents of the directory or the entire directory by adding or removing a trailing / from your input.

Here’s the description from the -R option of the man page:

If source_file designates a directory, cp copies the directory and the entire subtree connected at that point. If the source_file ends in a /, the contents of the directory are copied rather than the directory itself.

If you want to copy just the contents of the directory into another directory, add a trailing / to your input.

If you want to copy the contents of the directory and the directory folder itself into another directory, don’t add a trailing / :

$ ls directory-1 directory-2 $ ls directory-2 $ cp -r directory-1 directory-2 $ ls directory-2 directory-1 $ ls directory-2/directory-1 a.txt

Here you can see that because directory-2 already exists—and the input source didn’t have a trailing / —both the contents of directory-1 and the directory itself was copied into the destination.

How to prevent overwriting files with cp

By default, the cp command will overwrite existing files:

$ cat a.txt A $ cat directory-1/a.txt B $ cp a.txt directory-1/a.txt $ cat directory-1/a.txt A

There are two ways to prevent this.

The interactive flag

To be prompted when an overwrite is about to occur, you can add the -i or —interactive flag:

$ cp -i a.txt directory-1/a.txt overwrite directory-1/a.txt? (y/n [n])

The no-clobber flag

Or, to prevent overwrites without being prompted, you can add the -n or —no-clobber flag:

$ cat a.txt A $ cat directory-1/a.txt B $ cp -n a.txt directory-1/a.txt $ cat directory-1/a.txt B

Here you can see that thanks to the -n flag the contents of directory-1/a.txt were not overwritten.

Other options

There are many other useful options to pass to the cp command: like -v for «verbose» output or -f for «force.»

I highly encourage you to read through the man page for all of the other useful options.

If you liked this tutorial, I also talk about topics like this on Twitter, and write about them on my site.

Источник

Оцените статью
Adblock
detector