Копирование каталогов в Linux, только если он содержит указанный файл

Мне нужно сделать копию всех папок в / parent (в новое место), но только если в нем есть 123.dat - в этом случае мне тоже нужно скопировать папку, но ни один из других файлов, которые она содержит .

Так что это:

|parent
|    |a
|    |    123.dat
|    |    456.dat
|    |b
|    |    123.dat
|    |    789.dat
|    |c
|    |    456.dat
|    |    789.dat

становится:

|parent
|    |a
|    |    123.dat
|    |b
|    |    123.dat

Как мне сделать это в Linux? Эта область не является моей компетенцией, и до сих пор я не добивался успеха в попытках найти что-то подобное.

4
задан 22 July 2019 в 14:24

2 ответа

Файл/Структура папок:

$ find src | sort
src
src/a
src/a/123.dat
src/a/456.dat
src/b
src/b/123.dat
src/b/768.dat
src/c
src/c/456.dat
src/c/768.dat

Копия, соответствующая файлам, сохраняя относительный путь (мелкий, не идя глубже, чем 1 папка):

Команда:

$ (cd src && cp -v --parents */123.dat ../dest)

Вывод:

a -> ../dest/a
'a/123.dat' -> '../dest/a/123.dat'
b -> ../dest/b
'b/123.dat' -> '../dest/b/123.dat'
  • Я использовал подоболочку с ( и ) для не изменения исходного рабочего каталога при использовании cd. Я должен был войти src перед выполнением cp для не создания src/ как основывают dir в dest.
  • Не будет работать с количествами файла выше, чем предел аргумента удара (обычно вокруг 65k, если я не ошибусь),

Альтернативный подход (использование find с корректируемым пределом глубины):

Команда:

$ (cd src && find . -maxdepth 2  -type f -name '123.dat' -exec cp -v -t "../dest" --parents {} +)

Вывод:

./b -> ../dest/./b
'./b/123.dat' -> '../dest/./b/123.dat'
./a -> ../dest/./a
'./a/123.dat' -> '../dest/./a/123.dat'

Примечания:

  • Я использовал подоболочку с ( и ) для не изменения исходного рабочего каталога при использовании cd. Я должен был войти src перед выполнением find для не создания src/ как основывают dir в dest.
  • Я указал -type f удостоверяться только файлы с именем 123.dat рассматриваются, не каталоги, которые, оказывается, имеют то имя

Альтернативный подход (использование rsync, без ограничения глубины):

Команда:

$ rsync -rv --include=123.dat --include='*/' --exclude='*' --prune-empty-dirs src/ dest

Вывод:

building file list ... done
created directory dest
./
a/
a/123.dat
b/
b/123.dat

sent 205 bytes  received 90 bytes  590.00 bytes/sec
total size is 0  speedup is 0.00

Двойная проверка:

$ find dest
dest
dest/b
dest/b/123.dat
dest/a
dest/a/123.dat

Примечания:

  • Запаздывание / из src/ является намеренным, так, чтобы только содержание папки было скопировано, не сама папка.
  • --exclude='*' исключает все по умолчанию
  • --include='*/ переопределяет исключение и включает все папки
  • --include='123.dat' переопределяет исключение и включает файлы (и папки) с именами '123.dat'
  • --prune-empty-dirs удостоверяется, что никакие пустые папки не создаются (например. c)
5
ответ дан 23 November 2019 в 11:42

Один подход должен сделать новую версию родительского каталога в новом месте, затем скопировать через подкаталоги, если они содержат 123.dat. Это использует globbing функцию оболочки Bash, чтобы найти, что подкаталоги так будут только сразу работать над каталогами ниже родителя. в примере я предположу, что родитель находится в названном каталоге /location1/ и будет перемещаться в /location2/:

mkdir -p /location2/parent
for d in /location1/parent/*
  do if [[ -e "$d"/123.dat ]]; then
    cp -r "$d" /location2/parent
  done
fi

Как острота CLI, которая была бы:

mkdir -p /location2/parent; for d in /location1/parent/*; do if [[ -e "$d"/123.dat ]]; then cp -r "$d" /location2/parent; done; fi

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

Для копирования каталога, если это содержит конкретный файл но не копирует какой-либо из других файлов, существует меньше, чем изящное решение, которое требует cdлуг в исходный каталог, cd - часть в конце возвращает Вас Вашему исходному каталогу:

mkdir -p /location2/parent; cd /location1/parent/ && for d in ./*; do if [[ -e "$d"/123.dat ]]; then cp --parents "$d"/123.dat /location2/parent/; fi ; done; cd -

Как мультилиния:

mkdir -p /location2/parent
cd /location1/parent/ && for d in ./*
  do 
    if [[ -e "$d"/123.dat ]]; then 
      cp --parents "$d"/123.dat /location2/parent/
    fi
  done
cd -
6
ответ дан 23 November 2019 в 11:42

Другие вопросы по тегам:

Похожие вопросы: