Skip to main content


Create an empty directory structure that exactly duplicates an existing set of directories, but without any of the files from the original directory structure.

#Linux #DFIR #CommandLine #Trivia

Hal Pomeranz reshared this.

in reply to Hal Pomeranz

The simplest version would be

$ find . -type d -exec mkdir -p ${dest}{} \;

That will reproduce the directory names, but the ownership and permissions would all be the default so it's not "exactly duplicated". I could mess around with copying the directory instead of creating it, or build a subshell to try to extract the stat information from the original and pass that to mkdir, but there's another way.

First, construct a list of directories which we want to duplicate:

# cd ${SOURCE} ; find . -type d -print0

This will create a list of directories relative to ${SOURCE}, such as ".", "./moresource" and so on. If we gave find an absolute directory to start at, it would try to create a list of absolute paths instead. Now, feed that into a "tar" command to create an archive of all the directories complete with ownership and permissions:

# cd ${SOURCE} ; find . -type d -print0 | xargs -0 tar cf ${OUTPUT} --no-recursion

The "--no-recursion" flag means that tar will include the directory, but none of the files inside of it, which is what we want. The final step is to take that ${OUTPUT} file, copy it to wherever you need it, and run "tar xf' on it. If you're trying to do the whole thing on a single system you can shortcut the whole thing with a pipe:

# cd ${SOURCE} ; find . -type d -print0 | xargs -0 tar cf - --no-recursion | tar -C ${DESTINATION} -xf -

This removes the ${OUTPUT} file entirely and sends the archive to stdout. A second tar process reads from stdin and unpacks the archived data in the directory ${DESTINATION}.

If you need to recreate the directory structure on a remote server you can still do this in a single line by running the final tar command remotely:

# cd ${SOURCE} ; find . -type d -print0 | xargs -0 tar cf - --no-recursion | ssh ${REMOTE_SERVER} tar -C ${DESTINATION} -xf -

This entry was edited (1 year ago)
in reply to Hal Pomeranz

solution
find <src> -type d | cpio -o | (cd <dst> && cpio -idmu)
in reply to Hal Pomeranz

mkdir -p "${dest_dir}"
find "${src_dir}" -type d -printf "./%P\n" \
| cpio -D "${src_dir}" -p "${dest_dir}"
in reply to Hal Pomeranz

Is rsync allowed? Like:

rsync -r --dirs -f'+ **/' -f'- *' FROM/ TO/

in reply to Hal Pomeranz

The shitpost version is probably:
cp -r source dest
find dest -type f -delete
in reply to Chiasm

I would probably do a more like find src -type d -exec mkdir -p dest/{}, but a bunch of people already did versions that seem to have solved the minor bugs you'd find in that, so I went with the shitpost
in reply to Hal Pomeranz

ooh wait! I remember something.... (Scurries away.... Comes back)

Find . -type d -print0 | cpio --null -pvd newDir

in reply to Hal Pomeranz

Yesterday's Linux DFIR command line trivia again saw folks showing up with multiple different approaches.

First is the straightforward "find ... | xargs mkdir" or "find ... -exec mkdir" approach. I'll give @prograhamer credit for being the first to check in with this idea.

cd /orig/directory
find . -type d -print0 | (cd /target/dir; xargs -0 mkdir)

The subshell here lets us take the directory list output from the find command and execute "xargs mkdir" in the target directory location. We use "-print0" and "xargs -0" just in case we're dealing with directory names that contain spaces.

Then @apgarcia went all old-school and suggested "find ... | cpio":

cd /orig/directory
find . -type d | cpio -pdm /target/dir

cpio in passthru mode ("-p") takes a list of paths, one per line (no need for -print0) and copies them to the target destination. "-d" means make directories as needed and "-m" preserves last modified times.

@uriy jumped in with the rsync version:

rsync -r --dirs -f'+ **/' -f'- *' /orig/directory/ /target/directory/

rsync is recursively ("-r") copying the entire directory contents, making directories as needed ("--dirs"). However, the first filter matches just the directory names (paths ending in slash) and the second filter suppresses the other directory contents. The rsync solution has the advantage that you don't need to do a cd command at any point.

Finally, @silverwizard suggested simply doing a "cp -r" and then going back and deleting everything that wasn't a directory. I shall treat this solution with the seriousness it deserves...

#Linux #DFIR #CommandLine #Trivia

in reply to Hal Pomeranz

@prograhamer @uriy @silverwizard

just a footnote, it occurred to me that symlinks are often found within directory structures. perhaps an exercise for the reader could be to also copy only those symlinks that point to directories...

in reply to apgarcia

@apgarcia @prograhamer @silverwizard
For that case I'd use a slightly modified version of your sweet cpio solution:
cd /PATH/TO/FROM; find . -type d -o -xtype d | cpio -pdm /PATH/TO/DEST
so the test part of the find is now "is a directory, or is a symlink and what it points at is a directory"
in reply to Hal Pomeranz

Hm, dont only the last 2 get the permissions & owner/group right?

I couldnt figure out a way in one pass to do both create and chmod/own (having forgotten rsync and never used cpio).

in reply to smoot

@smoot I didn’t specify in the original question but I wasn’t so worried about perms/ownership/timestamps
in reply to Hal Pomeranz

you said "exactly" (-; Shoot I would have missed timestamps too.
in reply to smoot

@smoot Without low-level tools (debugfs, etc) you can’t set ctime or btime anyway. So there isn’t a whole lot of point in messing with timestamps in this scenario.
in reply to Hal Pomeranz

did figure out my initial idea tho: find DIR1 -type d -print0 | tar -cnf - -T - | tar -xf - -C DIR2