- How do I copy multiple files by wildcard?
- 6 Answers 6
- Update
- TEST
- TRANSFORM
- STREAM
- ARGUMENTS
- unixforum.org
- Re: bash копирование файлов по маске
- Re: bash копирование файлов по маске
- How to Copy Files with Specific File Extension Recursively
- Copy Files with Specific File Extensions
- Find and Copy Files with File Extension
- Команда cp: правильное копирование папок с файлами в *nix
- Выводы
- Послесловие
How do I copy multiple files by wildcard?
What is the simplest way to do this with a batch command (in BASH or similar)? I am thinking something involving sed or awk or xargs, but I’m having difficulty figuring out the syntax. I could write a Python script, but I’m thinking there is probably a command line solution that is not too complicated.
6 Answers 6
How about something like this in bash:
for file in ABC.*; do cp "$file" "$";done
you can test it by putting echo in front of the cp command:
for file in ABC.*; do echo cp "$file" "$";done
To do this efficiently with a large number of files, it is better to avoid a starting a different cp process for each one. One way would be to copy then rename them using prename ( rename is symlinked to this by default on Debian based distros). Using this and the Linux mktemp :
tmp=$(mktemp -d --tmpdir=.) cp ABC.* "$tmp" prename "s:$tmp/ABC:DEF:" "$tmp/"* rmdir "$tmp"
Update
Actually pax may be a better way to go here:
Unfortunately, despite pax being both POSIX and Linux Standard Base, few distros currently include it by default.
tar will do this for you really fast.
TEST
First I created 2 directories and 10 files:
% mkdir test1 test2 ; cd test1 % for n in `seq 1 10` ; do touch ABC.file$n ; done % ls > ABC.file1 ABC.file2 ABC.file4 ABC.file6 ABC.file8 > ABC.file10 ABC.file3 ABC.file5 ABC.file7 ABC.file9
% tar -cf - ./* |\ tar -C../test2 --transform='s/ABC/DEF/' -xf - % ls ../test2 > DEF.file1 DEF.file2 DEF.file4 DEF.file6 DEF.file8 > DEF.file10 DEF.file3 DEF.file5 DEF.file7 DEF.file9
TRANSFORM
So GNU tar will accept a sed —transform=EXPRESSION for file renaming. This can even rename only some of the files. For instance:
% tar -cf - ./* |\ tar -C../test2 --transform='s/ABC\(.*5\)/DEF\1/' -xf - % ls ../test2 > ABC.file6 ABC.file8 DEF.file1 DEF.file2 DEF.file4 > ABC.file7 ABC.file9 DEF.file10 DEF.file3 DEF.file5
STREAM
Also consider that this is only two tar processes — and that will not alter regardless of your file count.
tar is as optimized as you could want it to be. This will never have problem argument counts or runaway child processes. This is just A > B done.
ARGUMENTS
I use 7 distinct arguments combined between my two tar processes here. The most important one is listed here first:
— stdout/stdin — this informs tar that it will be streaming either its input or output to or from stdin/stdout which it will interpret correctly depending on whether or not it is building or extracting an archive.
-c create — this tells tar to build the archive. The next argument tar expects is.
-f file — we specify that tar will be working with a file object rather than a tape-device or whatever. And the file it will be working with, as noted above, is stdin/stdout — in other words, our |pipe .
./* all $PWD/files — not too much to explain here except that the archive argument comes first, so — then ./* .
. and on the other side of the |pipe .
-C change directory — this informs tar that it needs to change to the directory I specify before performing any other action, so effectively it just cd ../test2 before extraction.
—transform=’s/ed/EXPR/’ — as has already been mentioned, this did the renaming. But the docs indicate that it can take any sed expression or //flag .
-x extract — after tar changes to our target directory and receives our renaming instructions we instruct it to begin extracting all of the files into its current directory from the -f — |pipe archive file. No mystery.
unixforum.org
Здравствуйте, подскажите пж-та, только начинаю изучать bash и возник вопрос: написал простейший скрипт, который находит все файлы с расширением txt в каталоге /testmy. Если файлы найдены, то они копируются в каталог /testmy2. Если файлов с расширением txt нет, то выдается сообщение: файлов с расширением txt нет в каталоге и происходит выход из скрипта.
Так вот, скрипт корректно работает, если есть один файл с расширением txt. Если их 2 и более, то скрипт всегда возвращает «файлов с расширением txt нет в каталоге». В чем может быть проблема?
Сам скрипт:
#!/bin/bash
searcher=`find /testmy -name *.txt`
cd /testmy
if [[ ! -f $searcher ]]; then
echo «файлов с расширением txt нет в каталоге»
exit 0
else
cp $searcher /testmy2
echo «файлы с расширением txt скопированы»
fi
Bizdelnick Модератор Сообщения: 20387 Статус: nulla salus bello ОС: Debian GNU/Linux
Re: bash копирование файлов по маске
Сообщение Bizdelnick » 16.10.2017 17:10
Если файлов несколько, переменная $searcher будет содержать имена их всех, разделённые символом новой строки. Вы проверяете, есть ли файл с именем, совпадающим с содержимым (всем) этой переменной, а такого, разумеется, нет. Вместо этого достаточно проверить статус выхода find: если файлы найдены, он будет 0, если нет — отличным от 0.
Ещё один нюанс: символ * надо экранировать, иначе он может будет проинтерпретирован башем как шаблон и, если в текущем каталоге есть файл (или файлы), соответствующий шаблону, выполнится подстановка. В результате find будет искать файл с именем, совпадающим с именем файла из текущего каталога (а если таких файлов подставится несколько, выдаст сообщение об ошибке).
в консоли вку́пе (с чем-либо) в общем вообще | в течение (часа) новичок нюанс по умолчанию | приемлемо проблема пробовать трафик |
Re: bash копирование файлов по маске
Сообщение v1k3ng » 16.10.2017 17:16
#!/bin/bash searcher=`find /testmy -name *.txt` if [[ -z $searcher ]] then echo «файлов с расширением txt нет в каталоге» exit 0 else cp $searcher /testmy2 echo «файлы с расширением txt скопированы» fi
Не претендую на стопроцентную правильность и изящность, но так оно хотя бы работает.
Почему не работало у вас:
В переменную $searcher вы загоняли весь список файлов. Допустим, в директории было 3 файла с названиями 1.txt, 2.txt и 3.txt.
Тогда переменная $searcher перед условием выглядела бы «/testmy/1.txt /testmy/2.txt /testmy/3.txt» без кавычек. И вы спрашиваете в условии IF — а есть ли такой файл?? Разумеется такого файла нет. Когда у вас находился один файл, тогда и переменная выглядела как полный путь к файлу.
Я заменил в условии ! -f на -z (если длина строки нулевая, то. )
How to Copy Files with Specific File Extension Recursively
In Linux, the command ‘cp‘, which standards for ‘Copy‘ is used to copy files and folders to another folder. It is available by default in Linux as part of the GNU Coreutils set of tools.
The most basic use of the cp command is to specify the files to be copied as the arguments and to specify the target folder as the last argument.
$ cp file1 file2 file3. fileN target_folder/
Copy Files with Specific File Extensions
You can even copy files of the same file extension (Eg. .txt , .jpg , .mp4 ) together using wildcard characters, as shown below:
This will copy all the JPEG images, MP3, and MP4 multimedia files to the folder ‘media‘. Note that this can only be used for files. If you try to copy folders in the above format, it throws a warning that the folder is ‘Not empty‘.
To copy folders, we have to specify the ‘-r’ (recursive) flag. Recursive means that all the files in that folder, the files in the subfolders, and so on, will all be copied.
$ cp -r folder1/ folder2/ file1 file2 target_folder/
However, there is no way within ‘cp’ to copy files of a specific extension recursively. Whenever ‘-r’ is specified, the program always considers all files in the subfolders for copying.
Find and Copy Files with File Extension
To achieve this, we use the find command, which is simply used to search for files and folders in Linux based on the parameters of the file or folder: filename, extension, size, etc.
We will make use of the find command to recursively locate files with a specific file extension and then pass this output to cp command to copy them.
Syntax to locate files of a specific extension using find command is:
$ find -name ‘*. ’
For example, to locate all JPG files recursively in the current folder:
Finally, we have to pipe this output to the cp command. To do this, we use the ‘xargs’ command to make ‘cp’ consider the output of ‘find’ as its arguments. Also, we use the ‘-t’ flag of cp, to specify the target directory, without which the program considers the output of ‘find’ as the target directory.
$ find . -name '*.jpg' | xargs cp -t Pictures2/
Thus, all the files of the extension ‘.jpg’ have been copied to the folder ‘Pictures2’.
Conclusion
In this article, we learned how to copy files with a specific extension recursively in Linux. Note that this method is useful if you are dealing with a smaller number of files. For a huge number of files (for example, in tens of thousands), you need to use a different approach to copy the files recursively.
If you have any questions or feedback, let us know in the comments below.
Команда 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. Картинка очень большая и детальная, можно открыть в отдельном окне.