Поиск по всем каталогам и подкаталогам в текущем каталоге для файлов, которые соответствуют списку расширений файлов. Скопируйте эти файлы в новую директорию, поддерживающую файловую структуру - PullRequest
2 голосов
/ 17 апреля 2020

У меня есть текстовый файл с длинным списком расширений файлов:

.sln
.csproj
.cs
.xaml
.cshtml
.javasln
.project
.java
... (etc)

У меня есть три каталога проектов, каждый со многими подкаталогами.

Я могу получить список всех файлов и пути с: find . -type f -printf "%p\n"

./DirectoryA/src/main/resources/static/resources/build/home.ini
./DirectoryA/src/main/resources/static/resources/images/spring-pivotal-logo.png
./DirectoryB/src/main/resources/db/hsqldb/data.sql
./DirectoryC/src/main/resources/project/schema.project

Я хочу, чтобы l oop через них, и если расширение файла соответствует одному из моего списка, скопируйте его в myCopyDirectory, сохраняя при этом их структуру каталогов.

То есть, если ./DirectoryC/src/main/resources/project/schema.project расширение файла соответствует .project (как оно есть) как одно из расширений в моем текстовом файле ... скопируйте его в новый каталог, например ./myCopyDirectory, как: ./myCopyDirectory/DirectoryC/src/main/resources/project/schema.project.

Так что мне понадобится для или пока l oop, пожалуйста, прости мой ужасный псевдокод, но это мое видение

Сценарий 1: Копирование файлов на лету

for FILE in `find . -type f -printf "%p\n"`; do if [ ${FILE##*.} in extensions.txt ]; then mkdir -p ./myCopyDirectory/DirectoryC/src/main/resources/project/ && cp ./DirectoryC/src/main/resources/project/schema.project ./myCopyDirectory/DirectoryC/src/main/resources/project/schema.project

Сценарий 2: Создайте список файлов, которые соответствуют и скопируйте их

for FILE in `find . -type f -printf "%p\n"`; do if [ ${FILE##*.} in extensions.txt ]; then echo $FILE >> listOfFiles.txt

for FILE in `cat listOfFiles.txt`; do filename="${FILE##*/}" && dir="${FILE:0:${#FILE} - ${#filename}}" && if [ -e ./myCopyDirectory/$dir ]; then mkdir -p ./myCopyDirectory/$dir && cp $FILE ./myCopyDirectory

Сценарий 3: Просто cp -R все три папки в ./myCopyDirectory и вырвите все файлы, которые не соответствуют сопоставьте расширения в файле extensions.txt

Извините, пожалуйста, мой ужасный псевдокод. Я просто пытаюсь сделать эту работу и немного не в себе. Я мог бы создать скрипт PERL или Python, но это кажется ненужным.

Ответы [ 5 ]

2 голосов
/ 17 апреля 2020

Вам на самом деле не нужен цикл for; на самом деле, вы можете воспользоваться опцией find -exec, чтобы передать ей даже сложную команду оболочки для выполнения процедуры копирования, сохраняющей каталог.

Должна работать следующая однострочная строка (пояснение ниже).

find root1 -regex '.*\.\(ext1\|ext2\)$' -exec sh -c 'dir=${1%/*}; dir=${dir/root1/root2}; file=${1##*/}; mkdir -p $dir && cp $1 $dir/$file' _ {} \;

Я проверил его, создав следующее дерево каталогов в качестве примера,

$ mkdir root1
$ mkdir root1/sub
$ mkdir root1/sub/dir
$ touch root1/a.ext1 root1/a.ext2 root1/a.ext3 root1/sub/a.ext1 root1/sub/a.ext2 root1/sub/a.ext3 root1/sub/dir/a.ext1 root1/sub/dir/a.ext2 root1/sub/dir/a.ext3
$ tree root1/
root1/
├── a.ext1
├── a.ext2
├── a.ext3
└── sub
    ├── a.ext1
    ├── a.ext2
    ├── a.ext3
    └── dir
        ├── a.ext1
        ├── a.ext2
        └── a.ext3

2 directories, 9 files

, а затем выполнив команду и проверив результат

$ find root1 -regex '.*\.\(ext1\|ext2\)$' -exec sh -c 'dir=${1%/*}; dir=${dir/root1/root2}; file=${1##*/}; mkdir -p $dir && cp $1 $dir/$file' _ {} \;
$ tree root2
root2
├── a.ext1
├── a.ext2
└── sub
    ├── a.ext1
    ├── a.ext2
    └── dir
        ├── a.ext1
        └── a.ext2

2 directories, 6 files
  • Параметр -regex используется для поиска файлов с расширением ext1 или ext2;
  • Параметр -exec используется для выполнения следующего sh ell команда для каждого найденного файла;
  • этой команде передается командная строка через параметр -c, а затем фиктивный указатель _ для параметра 0 и {}, который является именем файл, найденный с помощью find, для параметра 1;
  • командная строка оболочки
    • извлекает каталог dir каждого файла, удаляя последний / и все остальные следует из $1 (что было ssed {}),
    • затем изменяет его, подставляя root2 для root1;
    • аналогичным образом, извлекает имя файла file;
    • , наконец, оно создает новую структуру каталогов с mkdir и копирует в нее файл.

Я не включил параметр -type f, но вы можете, если у вас действительно есть папки имена с расширением, равным одному из тех, которые вы ищете.

1 голос
/ 17 апреля 2020

Это может сработать для вас (GNU параллельно и найти):

find . -type f |
parallel --rpl '{d} s:.*?/::;s:/[^/]*$::' \
  'mkdir -p myCopyDirectory/{1d} && \
   [ {1} = {1.}{2} ] && \
   cp -v {1} myCopyDirectory/{1d}/{1/}' :::: - :::: ../fileExts.txt

Используйте команду find для распечатки только файлов в / в текущем каталоге.

Передача результирующего файла в параллельную команду в качестве параметра 1.

Определение параллельной строки замены с именем {d}, которая удаляет верхний каталог и имя файла из входной строки.

Создайте каталог в текущий с использованием вышеуказанных строк (используйте параметр -p для принудительного создания промежуточных каталогов).

Проверьте текущий файл на соответствие требуемым расширениям файла в качестве параметра 2 (сохраните эти расширения в текстовом файле в каталог выше текущего или где-либо еще).

Если расширения совпадают, скопируйте текущий файл в созданную резервную копию, используя параметр verbose, чтобы увидеть файлы, которые копируются (это можно удалить для тихий бег).

В целях тестирования используйте опцию --dryrun параллельно, чтобы увидеть сгенерированные команды и после утверждения опцию можно удалить.

1 голос
/ 17 апреля 2020

Это, кажется, работает хорошо. Спасибо всем, кто помог. Пожалуйста, улучшения и предложения приветствуются! Еще раз спасибо.

find ./myDirToSearch -type f -regex ".*\.\(sln\|csproj\|cs\|xaml\|cshtml\|javasln\|project\|java\)" -exec cp --parents \{\} ./myCopyDir \;
1 голос
/ 17 апреля 2020

Вы можете попробовать find с while read loop плюс некоторые функции оболочки.

#!/usr/bin/env bash

shopt -s extglob

##: If bash is lower that v4, one alternative is.
##: while read -r lines; do extensions+=("${lines#*.}"); done < file_with_extension.txt

##: This assumes that the file_with_extensions.txt is in the same
##: directory as the files/directory that you're going to process, 
##: change the correct path e.g. /path/to/file_with_extension.txt

mapfile -t extensions < file_with_extension.txt

##: Add as much directory you need.
Dirs=(
  ./DirectoryC/src/main/resources/project/
  ./DirectoryB/src/main/resources/db/hsqldb/
  ./DirectoryA/src/main/resources/static/resources/images
  ./DirectoryA/src/main/resources/static/resources/build
  /AnotherDirectory/From/another/Path
  /A/Not/So/distant/Directory/From/Far/Far/Away
  /One/Directory/To/Rule/Em/All
)

ext=$(IFS='|'; printf '%s' "*.@(${extensions[*]#*.})" )

dest=./myCopyDirectory

while IFS= read -d '' -r files ; do
  if [[ $files = $ext ]]; then
     echo mkdir -p "$dest/${files%/*}" && echo cp -v "${files}" "$dest/${files%/*}"
  fi
done < <(find "${Dirs[@]}" -type f -print0)

  • Удалите echo, если считаете, что вывод правильный.

  • Это должно быть защищено от пробелов, табуляции и новых строк в имени файла и пути, это единственное преимущество от другого поста по этому вопросу.

  • Недостаток? Требуется bash4+ из-за mapfile (см. Комментарий к сценарию для обходного решения), и это не oneliner: -)


Пример

mkdir -p /tmp/testing123 && cd /tmp/testing123
mkdir -p ./DirectoryC/src/main/resources/project/
mkdir -p ./DirectoryB/src/main/resources/db/hsqldb/
mkdir -p ./DirectoryA/src/main/resources/static/resources/images
mkdir -p ./DirectoryA/src/main/resources/static/resources/build/
touch ./DirectoryC/src/main/resources/project/schema.project
touch ./DirectoryB/src/main/resources/db/hsqldb/data.sql
touch ./DirectoryA/src/main/resources/static/resources/images/spring-pivotal-logo.png
touch ./DirectoryA/src/main/resources/static/resources/build/home.ini

Убедитесь, что script выше и files_with_extensions.txt находятся в той же директории, что и ваш текущий pwd / cwd.

Запустите скрипт.

./myscript

Вывод

'DirectoryA/src/main/resources/static/resources/images/spring-pivotal-logo.png' -> './myCopyDirectory/DirectoryA/src/main/resources/static/resources/images/spring-pivotal-logo.png'
'DirectoryA/src/main/resources/static/resources/build/home.ini' -> './myCopyDirectory/DirectoryA/src/main/resources/static/resources/build/home.ini'
'DirectoryB/src/main/resources/db/hsqldb/data.sql' -> './myCopyDirectory/DirectoryB/src/main/resources/db/hsqldb/data.sql'
'DirectoryC/src/main/resources/project/schema.project' -> './myCopyDirectory/DirectoryC/src/main/resources/project/schema.project'

Проверка каталога / файлов myCopyDirectory

find myCopyDirectory/ -type f

Вывод

myCopyDirectory/DirectoryC/src/main/resources/project/schema.project
myCopyDirectory/DirectoryB/src/main/resources/db/hsqldb/data.sql
myCopyDirectory/DirectoryA/src/main/resources/static/resources/images/spring-pivotal-logo.png
myCopyDirectory/DirectoryA/src/main/resources/static/resources/build/home.ini

Давайте разберемся с ним.

  • shopt -s extglob Включите функцию оболочки, чтобы тест в [[ ]] работал.

  • mapfile -t extensions < file_with_extension.txt Сохраняет расширения файла из файла в массив с именем extensions

  • ext=$(IFS='|'; printf '%s' "*.@(${extensions[*]#*.})" ) Форматирует массив с именем extension, используя значение IFS в структуре extglob понять внутри [[ ]] теста. #*. удаляет начальную . точку из каждого элемента / записи из массива.

  • dest=./myCopyDirectory Сохраняет ведущую структуру каталогов в переменной с именем dest

  • while IFS= read -d '' -r files По умолчанию read убирает начальные и конечные пробелы, поэтому IFS= (значение по умолчанию) необходимо для отключения этой функции. -d '' безопасен для ввода null с разделителями, -r защищен от ввода с косой чертой.

  • [[ $files = $ext ]] Если файлы из find соответствуют расширению из списки, которые были преобразованы в массив и были преобразованы в формат, понятный extglob.

  • mkdir -p "$dest/${files%/*}" Создайте ведущую структуру каталогов плюс структуру каталогов соответствующего файла. -p избавляет вас от многих неприятностей и ошибок, см. mkdir --help или info mkdir или man mkdir

  • cp -v "${files}" "$dest/${files%/*}" Копировать (-v многословно) совпадающее файл в новый созданный каталог с желаемой структурой. ${files%/*} удаляет завершающий / из результатов поиска, и поскольку / не может находиться в file name (по крайней мере, с файловыми системами, которые я использую). Гарантируется, что вы ' просто удаляем путь из имени файла.

  • < <(find "${Dir[@]}" -type f -print0) <() Называется Process Substitution. "${Dirs[@]}" расширится на ВСЕ элементы, так как это массив. -type f Убедится, что вас интересуют только обычные файлы, а не каталоги и так далее. -print0 Выводит структуру null с разделителями.

0 голосов
/ 17 апреля 2020

Это решение считывает расширения файла для команды find из файла: "./extensions.txt".

mapfile -t < ./extensions.txt; exts=$(IFS='|'; printf '%s' "${MAPFILE[*]#*.}" ); exts=`echo $exts | sed 's/|/\\\|/'`; find ./myDirToSearch -type f -regex ".*\.\($exts\)" -exec cp --parents {} ./myCopyDir \;

То же, но разделены на несколько строк, чтобы их было легче читать:

mapfile -t < ./extensions.txt; \
exts=$(IFS='|'; printf '%s' "${MAPFILE[*]#*.}" ); \
exts=`echo $exts | sed 's/|/\\\|/'`; \
find ./myDirToSearch -type f -regex ".*\.\($exts\)" -exec cp --parents {} ./myCopyDir \;

Или вместо bash можно использовать awk для установки "exts":

exts=`awk 'BEGIN { FS = "." } \
    { exts[NR] = $2 } \
    END { i = 0; \
        for (key in exts) { \
            printf "%s", exts[key]; \
            if (++i != NR) { printf "\\\|" } \
         } \
         printf "\n" \
    }' ./extensions.txt`; \
find ./myDirToSearch -type f -regex ".*\.\($exts\)" -exec cp --parents {} ./myCopyDir \;

Или для тех, кто на нас на Ма c (обратите внимание на -E опция для find и удаление экранированных символов в регулярном выражении и exts bash переменная):

exts=`awk 'BEGIN { FS = "." } { exts[NR] = $2 } END { i = 0; for (key in exts) { printf "%s", exts[key]; if (++i != NR) { printf "|" } } printf "\n" }' ./extensions.txt`; find -E ./myDirToSearch -type f -regex ".*\.($exts)" -exec bash -c 'dir=`dirname {}`; dir=./myCopyDir/$dir; mkdir -p $dir; cp {} $dir' \;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...