21

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.

26

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
  • 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 '15 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? – SexyBeast Jul 13 '16 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 '16 at 16:55
  • Can this be done to create relative links? – ctrl-alt-delor Oct 28 '16 at 15:32
  • @richard if SOURCE is a relative path then I think so. Try it and if this doesn't work I suggest you might want to ask a new question. Reference this one by all means. – roaima Oct 29 '16 at 6:22
6

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
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

Something like this will do what you need.

#!/bin/bash
#
SOURCE="$1" COPY="$2"
cd "$SOURCE"
find . |
    sed 's!^\./!!' |
    while IFS= read ITEM
    do
        test -d "$ITEM" && { mkdir -p "$COPY/$ITEM"; continue; }
        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 cp and cpio but neither seems to have an option for linking to the source with symbolic links.)

  • 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 '16 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 '16 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
    Since you're assuming GNU cp anyway, why not cp -as? – Gilles Apr 16 '15 at 23:58
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.)

  • 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 '16 at 17:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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