23

Is there a way to copy an entire directory, but only the folders? I have a corrupt file somewhere in my directory which is causing my hard disks to fail.

So instead of copying the corrupt file to another hard disk, I wanted to just copy the folders, because I have scripts that search for hundreds of folders, and I don't want to have to manually create them all.

I did search the cp manual, but couldn't see anything (I may have missed it)

Say I have this structure on my failed HDD:

dir1
    files
    dir2
        files
        files
        dir4
dir3
files

All I a want is the directory structure, not any files at all.

So I'd end up with on the new HDD:

dir1
    dir2
        dir4
dir3

Hoping someone knows some tricks!

3
  • Do you mean you want to just copy the directory strucure or do you just mean you want to exclude the files in the current working directory (and all other files in deeper subdirectories are game)?
    – Oli
    Oct 25, 2013 at 21:37
  • I have added an example. sorry for being confusing
    – Shannon
    Oct 25, 2013 at 21:46
  • 1
    FYI use rsync to copy the directory structure AND retain the permissions and attributes](stackoverflow.com/a/9242883/52074). Using plain mkdir -p does not preserve permissions and attributes. Jun 1, 2017 at 20:24

2 Answers 2

26

If you want to mirror a directory skeleton and copy no files:

find -type d -links 2 -exec mkdir -p "/path/to/backup/{}" \;

What's going on here:

  • Find is only selecting directories
  • We're using -links 2 to find the deepest possible directories.
  • mkdir -p will make any missing directories along the way.

I've done it like this rather than find -type d -exec mkdir -p "/path/to/backup/{}" \; because it'll cut out on a whole buttload of mkdir calls. We can quickly prove this with a little test. Here's the test tree followed by what I ran to compare the two commands:

$ tree
.
├── dir1
│   ├── dir2
│   │   └── dir3
│   ├── dir7
│   └── dir8
└── dir9
    └── dir0

$ pr -m -t <(find -type d) <(find -type d -links 2)
.                               ./dir1/dir8
./dir1                          ./dir1/dir2/dir3
./dir1/dir8                     ./dir1/dir7
./dir1/dir2                     ./dir9/dir0
./dir1/dir2/dir3
./dir1/dir7
./dir9                  
./dir9/dir0 

And that's only going to get better in a real-word solution with thousands of directories.

8
  • This is me being a noob, however how are you searching the old dir to copy to the backup? I can only see one path here
    – Shannon
    Oct 25, 2013 at 21:48
  • If you don't specify a path, find uses the current working directory as the starting point. If you wish to use another path use the syntax: find /path/to/search/ -type.....
    – Oli
    Oct 25, 2013 at 21:51
  • So I set my directory to a certain folder that contains the skeleton I want to copy, run find -type d -exec mkdir -p "/media/jinglez/TVSeries/TV Series"/{}" \; and all should be okay? It seems to be taking a while :/
    – Shannon
    Oct 25, 2013 at 22:03
  • I run this from normal terminal right?
    – Shannon
    Oct 25, 2013 at 22:03
  • 1
    They're both specific to find's exec clause. {} means the current "found" filename and \; is just the escape to end the clause. By the looks of it, there's an extra " in your code up to no good, I'm sure... Should read: find -type d -links 2 -exec mkdir -p "/media/jinglez/TVSeries/TV Series/{}" \;
    – Oli
    Oct 25, 2013 at 22:06
3

Quite easily done with python one-liner:

bash-4.3$ tree
.
├── ABC
├── set_pathname_icon.py
├── subdir1
│   ├── file1.abc
│   └── file2.abc
├── subdir2
│   ├── file1.abc
│   └── file2.abc
└── subdir3
    └── subdir4
        └── file1.txt

4 directories, 7 files
bash-4.3$ python -c 'import os,sys;dirs=[ r for r,s,f in os.walk(".") if r != "."];[os.makedirs(os.path.join(sys.argv[1],i)) for i in dirs]' ~/new_destination
bash-4.3$ tree ~/new_destination
/home/xieerqi/new_destination
├── subdir1
├── subdir2
└── subdir3
    └── subdir4

As script this could be rewritten like so:

#!/usr/bin/env python
import os,sys
dirs=[ r for r,s,f in os.walk(".") if r != "."]
for i in dirs:
    os.makedirs(os.path.join(sys.argv[1],i)) 

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .