30

I have a folder SOURCE that contains several sub-level folders, each with its own files.

I want to copy this folder in a new folder COPY where I need to copy the directory structure but keep the files as symbolic links to the original files in SOURCE and its subfolders.

2

8 Answers 8

40

Here's the solution on non-embedded Linux and Cygwin:

cp -as SOURCE/ COPY

Note that SOURCE must be an absolute path and have a trailing slash. If you want to give a relative path, you can use

cp -as "$(pwd)/SOURCE/" COPY
8
  • Works perfect, thank you. Just a note.. SOURCE has to be given with full path (COPY can instead be given in relative terms if one wishes)
    – Antonello
    Apr 17, 2015 at 7:36
  • How to make it work if the source directory will be constantly updated with new folders and new files inside them? Will symlinks to them be automatically created in the destination directory in correct folder structure? Jul 13, 2016 at 18:29
  • @AttitudeMonger No, such a command as cp just creates the links once (as you can see from the result). To update the destination automatically you would have to monitor the source directory for changes.
    – user905686
    Jul 26, 2016 at 16:55
  • Can this be done to create relative links? Oct 28, 2016 at 15:32
  • 1
    If you want the end symlink to turn out to be a relative path, use this: cp -as "$PWD/src" "path/to/dest" && symlinks -rc "path/to/dest"
    – phyatt
    Jan 4, 2017 at 18:05
9

There are at least 2 standard utilities to build a shadow directory tree of an existing tree, so no need to write code here.

First there's lndir(1) from the xutils-dev package. It uses symlinks to files. From the man page:

NAME
   lndir  -  create a shadow directory of symbolic links to another
             directory tree
SYNOPSIS
   lndir [ -silent ] [ -ignorelinks ] [ -withrevinfo ] fromdir [ todir ]

A perhaps better alternative is to simply use cp with the right options as the accepted answer suggests. I'll just give some more hopefully useful detail:

cp -al /src/dir /dest/dir    # hard-links to leaf-files
cp -as /src/dir /dest/dir    # symlinks to leaf-files

If you don't care about preserving all attributes (ownerships/permissions, times) replace the a option (equivalent to -dr --preserve=all) with r (recursive only):

cp -rl /src/dir /dest/dir    # hard-links to leaf-files
cp -rs /src/dir /dest/dir    # symlinks to leaf-files
1
2

You can try a couple of find commands like this:

mkdir FULL-PATH-TO-COPY
cd SOURCE
find . \( ! -regex '\.' \) -type d -exec mkdir FULL-PATH-TO-COPY/{} \;
find * -type f -exec ln -s `pwd`/{} FULL-PATH-TO-COPY/{} \;
1

GNU shtool provides such a feature:

shtool mkshadow srcdir destdir

Moreover, if your point is to make a shallow copy of a source directory into a build tree, you can distribute shtool with your source tree as it is a portable shell script (which provides many other facilities you might be interested in).

1

Something like this POSIX script will do what you need.

#!/bin/sh
#
SOURCE="$1" COPY="$2"
cd "$SOURCE"
find . |
    sed 's!^\./!!' |
    while IFS= read -r ITEM
    do
        if test -d "$ITEM"
        then
            mkdir -p "$COPY/$ITEM"
            continue
        fi
        BASE="${FILE%\/*}"
        ( cd "$COPY/$BASE" && ln -s "$SOURCE/$ITEM" )
    done

Directories are created in the target COPY tree. Everything else is symlinked back to the absolute path within the SOURCE tree. Ensure that both SOURCE and COPY are specified as absolute paths (starting with /).

If you have a large tree to copy and you want a per-directory progress report, you could add echo "$ITEM" >&2; just before the mkdir command.

(I looked at POSIX cp and cpio but neither seems to have an option for linking to the source with symbolic links.)

2
  • 1
    The "line 9 test" comment may have related to ksh on AIX wanting a semicolon to close the brace-enclosed list: { mkdir -p "$COPY/$ITEM"; continue; }
    – Jeff Schaller
    Aug 4, 2016 at 16:18
  • @JeffSchaller thanks. I didn't write a ksh script... but it looks like that's necessary for bash too. Fixing
    – roaima
    Aug 4, 2016 at 16:20
0

If there are not empty dirs in SOURCE which need to be copied

find /full/path/to/SOURCE -type f -exec cp -t COPY --parents -s {} +
mv COPY/full/path/to/SOURCE COPY
rm -r COPY/full
1
0

I would start by breaking out the perl:

#!/usr/bin/perl

use strict;
use warnings;
use File::Find;

my $src_dir = "/full_path/to/dir";
my $tgt_dir = "/path/to/new/location";

sub link_or_mkdir {

    #current file is directory
    if (-d) {
        #extract the path
        my $newpath = $File::Find::dir;
        #change the path so 'old' and 'new' are swapped
        $newpath =~ s,^$src_dir,$tgt_dir,g;
        #print the command to make a new dir (doesn't actually do it)
        print "mkdir -p $newpath\n";
    }
    if (-f) {
        my $new_file = $File::Find::name;
        #change the path so 'old' and 'new' are swapped
        $new_file =~ s,^$src_dir,$tgt_dir,g;
        #print the symlink command
        print "ln -s $File::Find::name $new_file\n";
    }
}

find( \&link_or_mkdir, $tgt_dir );

File::Find is a useful module that allows you to run a specific subroutine on any file within a directory tree. In this case, the sub checks if it's a directory - and if it is, does a mkdir or a file - in which case it does a symlink.

-1

It seems you are looking for something like the tool rsnapshot; it creates copies of arbitrary directories and uses hardlinks where possible. (Have a look at the man page to see whether that fits.)

1
  • No, the question explicitely asks for symlinks and rsnapshot uses hardlinks (as it is a backup tool and the files should be kept even if they are removed from the source directory).
    – user905686
    Jul 26, 2016 at 17:10

You must log in to answer this question.

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