22

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!

Shannon
  • 231
  • 1
  • 2
  • 5
  • 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 '13 at 21:37
  • I have added an example. sorry for being confusing – Shannon Oct 25 '13 at 21:46
  • 1
    FYI use `rsync` to copy the directory structure AND retain the permissions and attributes](https://stackoverflow.com/a/9242883/52074). Using plain `mkdir -p` **does not preserve permissions and attributes**. – Trevor Boyd Smith Jun 01 '17 at 20:24

2 Answers2

23

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.

Oli
  • 289,791
  • 117
  • 680
  • 835
  • 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 '13 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 '13 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 '13 at 22:03
  • I run this from normal terminal right? – Shannon Oct 25 '13 at 22:03
  • What does putting the path in quotes and the {} at the end of it mean? Also the \; I have never seen that in a terminal command before – Shannon Oct 25 '13 at 22:04
  • 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 '13 at 22:06
  • I also missed the forward slash after the TV Series folder :) WOrked like a charm, thanks so much! – Shannon Oct 25 '13 at 22:07
  • Please note this solution may copy the directory structure but this solution does **not retain the directories' permissions and attributes**. Use `rsync` to copy the directory structure AND retain the permissions and attributes](https://stackoverflow.com/a/9242883/52074). – Trevor Boyd Smith Jun 01 '17 at 20:23
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)) 
derHugo
  • 3,306
  • 5
  • 30
  • 49
Sergiy Kolodyazhnyy
  • 103,293
  • 19
  • 273
  • 492