Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

Is there a command like

mv --preserve-structure src src/1 src/2/3 dst

which creates dst/1 and dst/2/3? It should work similar to mv src/* dst, but move only the subtrees listed.

share|improve this question
you can do this with rsync. – mattdm Mar 21 '11 at 17:53
So, you want each specified dir without descending into subdirectories? – glenn jackman Mar 21 '11 at 18:52
@glenn jackman Actually both, I'm moving a couple of things around like crazy. – maaartinus Mar 21 '11 at 23:16
up vote 0 down vote accepted

Under Linux, using rename from the Linux utilities (rename.ul under Debian and Ubuntu):

rename src dst src/1 src/2/3     # dst/2 must exist

With the rename Perl script that Debian and Ubuntu install as prename or rename:

rename 's!^src!dst!' src/1 src/2/3    # dst/2 must exist
rename 'use File::Basename; use File::Path;
        s!^src!dst! && mkpath(dirname($_))' src/1 src/2/3

Here's a shell function that does what you're asking except for the argument order:

mv-preserving-structure () {
  s=${1%/} t=${2%/}; shift 2
  for x; do
    case $x in
        mkdir -p -- "${y%/*}"
        mv -- "$x" "$t${x#$s}";;
mv-preserving-structure src dst src/1 src/2/3
share|improve this answer
> tar -cf - src/1 src/2/3 | (mkdir dst; cd dst; tar -xv --strip-components=1  -f -)
share|improve this answer
Overly complicated for a simple copy, but ok. Except the question was about moving the files. – Gilles Mar 21 '11 at 20:10
Also, that will take every subdirectory under src/1 etc, which I believe the OP does not want. – glenn jackman Mar 21 '11 at 20:47
@glenn jackman, I think he does want to move the whole src/1 subtree although I agree with @Giles that this is copying, not moving, so not really a solution. – JRW Mar 21 '11 at 21:52
Actually, I'm interested in both mv and cp. Both with or without subdirectories. So this solution is useful, too. – maaartinus Mar 21 '11 at 23:13

Assumes bash and GNU find.

mv_preserve_structure() {
  local src_file="$1"
  local dest="$2"
  local rel_dir="$(dirname "$src_file")/"
  rel_dir="${rel_dir#*/}"  # returns "2/3" if rel_dir was "src/2/3"
  mkdir -p "$dest/$rel_dir"
  mv "$src_file" "$dest/$rel_dir"

# unlike mv, this requires the destination dir as the first parameter
mv_dirs() {
  local dest="$1"
  for dir in "$@"; do
    for filename in "$dir"/*; do
      [[ -f "$filename" ]] && mv_preserve_structure "$filename" "$dest"

mv_dirs dst src src/1 src/2/3
share|improve this answer
I don't get the point of using find. – Gilles Mar 21 '11 at 20:10
@Gilles, just to get a list of files only at the level of the directory. updated. – glenn jackman Mar 21 '11 at 20:58

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.