How to copy all folders that contain a specific file

I want to backup only my FLAC music folders. FLAC files could be nested like that inside the folders:

AlbumName/
├── Files/
│   ├── someSong01.flac
│   ├── someSong02.flac
├── Covers/
│   ├── someCover01.jpg
│   └── someCover02.jpg

How do I copy and move all AlbumName's folders with their corresponding structure and content that contain somewhere inside at least one FLAC file (I'll assume this is enough to say: the music is in FLAC format)

EDIT: FLAC files could be nested; so I can have:

AlbumName2/
├── someSong01.flac
├── someSong02.flac
├── Covers/
│   ├── someCover01.jpg
|   └── someCover02.jpg

And I want to copy those folders with all their contents, not only FLAC files, and paste to another directory.

So if I have as well

AlbumName3/
├── someSong01.mp3
├── someSong02.mp3
├── Covers/
│   ├── someCover01.jpg
|   └── someHiddenSong.flac

and

AlbumName4/
├── Files/
│   ├── someSong01.mp3
│   ├── someSong02.mp3
├── Covers/
│   ├── someCover01.jpg
│   └── someCover02.jpg

I want to cp recursively to another directory AlbumName, AlbumName2 and AlbumName3 but not AlbumName4

EDIT: None of the answers were really doing what I want, so I ended up using something like that:

 find -mindepth 2 -name '*.flac' -exec dirname {} \; | awk -F "/" '{print $2}' | sort -u | while read -r dirname; do cp -r "$dirname" "backup/"; done

basically I list all flac files, I retrieve the root folder using awk, I delete the duplicates and I do what I want


Solution 1:

An option is to use rsync, which copies only flac files and preserves directory structure:

rsync -avzm --include=*/ --include=*.flac --exclude=* albums/ backup/
  • a archive
  • v verbose
  • z compress during transfer (may not be useful copying on the same computer)
  • m prune empty dirs
  • first include includes all directories
  • second include includes flac files
  • the last exclude excludes all other files

Solution 2:

hi my friend you can use

mkdir newdirectory
cp -r --parents */*.flac newdirectory/

Solution 3:

Nice answers

I want to add one more way, you can also use a combination of find and cpio

find . -name "*.flac" -print0|cpio --null -pdm destination/

Explanation:

GNU find searches the directory tree rooted at each given file name by evaluating the given expression from left to right, according to the rules of precedence (see Operators), until the outcome is known (the left-hand side is false for AND operations, true for OR), at which point find moves on to the next file name.

GNU cpio is a tool for creating and extracting archives, or copying files from one place to another. It handles a number of cpio formats as well as reading and writing tar files.

There are 3 cpio modes:

  • Copy-out mode: In copy-out mode, cpio copies files into an archive. It reads a list of filenames, one per line, on the standard input, and writes the archive onto the standard output.

  • Copy-in mode: In copy-in mode, cpio copies files out of an archive or lists the archive contents. It reads the archive from the standard input.

  • Copy-pass mode: In copy-pass mode, cpio copies files from one directory tree to another, combining the copy-out and copy-in steps without actually using an archive. It reads the list of files to copy from the standard input; the directory into which it will copy them is given as a non-option argument.

Here, we are using Copy-pass mode.

  • Options Used in above command:

find:

  • -name -> filename or use regex instead of writing full-name

cpio:

  • '-m, --preserve-modification-time' Retain previous file modification times when creating files.
  • '-p,--pass-through' Run in copy-pass mode. see ``Copy-pass mode``.
  • '-d, --make-directories' Create leading directories where needed.'
  • --quiet' Do not print the number of blocks copied.`

New options are the "-print0" available with GNU find, combined with the "--null" option of cpio. These two options act together to send file names between find and cpio, even if special characters are embedded in the file names.

One can also use rsync or write a shell script to find and copy files with the directory structure.

For Rsync explanation, Please have a look at above answers.

Solution 4:

Answer 1: You can also use find command for same.

mkdir newDir && find AlbumName/ -iname "*.flac" -exec cp --parents "{}" newDir/ \;

Explaination:

mkdir creates new directory. find command finds *.flac files located in AlbumName folder. exec command executes cp command to each of the filename that find has returned.

Answer 2: You can also use find command with xargs as well

mkdir newDir && find AlbumName/ -iname "*.flac" | xargs -I{} cp --parents {} newDir/

More information: find, xargs