3

I'm trying to run a recursive symlink from one directory into another:

find data/* -type d -exec ln -s {} current/{} \;

With one addition: I need to strip off data/ from the prefix.

Running on OS X server (10.8, Mountain Lion)--not all standard GNU commands, like cp -rs, are supported.

What I mean by recursively:

data is a list of persistent directories between Laravel releases:

data/
    \ storage/
        - framework/
            - session/
        - app/
        \ logs/

They need to map to:

current/
    \ storage/
        - framework
            - session/
        - app/
        - logs/
      # Also in storage, but we do NOT want to persist
        - debugbar/
        - framework/
            - cache/
            - views/

Our data directory is will be persistent storage between application launches, as we update our site, while retaining previous versions of the site in the event of rollbacks (current happens to be a softlink to the latest release).

Note: we also have other sites other than Laravel. data is going to be our standard, and we're going to match directory restructure depending on what the site requires for persistence. It won't always be data/storage.

guice
  • 976
  • 4
  • 11
  • 31
  • Can you explain what you mean by "recursive"? Once you've created a link from `current/foo` to `data/foo` (for example), all of its subdirectories are already accessible through that link… you don't need to do anything additional. –  May 19 '17 at 00:16
  • @duskwuff - already made it obvious, don't recurse. You can simply `find data -maxdepth 1 -type d -exec ln -s ../{} current \;` – alvits May 19 '17 at 00:51
  • @duskwuff: `data/foo/bar` => `current/foo/bar`, `data/foo/bartwo/play` => `current/foo/bartwo/play` – guice May 19 '17 at 16:48

3 Answers3

5

You don't need to do any recursion. Once you've linked to a directory, all of the files and directories under it are accessible through the link.

The only links you need are to the directories at the top level…

cd current
ln -s ../data/*/ .

Or, depending on your requirements, making current a link to data might even be sufficient.

  • Wrong. You'll end up with `current` having links that points to nowhere. Try `cd current; ln -s ../data/*/ .`. – alvits May 19 '17 at 00:56
  • @alvits Thanks, fixed. (Or just use an absolute path to `current`.) –  May 19 '17 at 00:58
  • My apologies, I failed to mention the `data` directory is not a complete set of directories, just a subset of what's to be shared. I'll update the post. – guice May 19 '17 at 16:47
  • @duskwuff Oh and I tried that. Didn't work because `current` is a softlink to `release/{revnum}/` - resulted in `../data` not found, due to the resolved pathing of softlinks. – guice May 19 '17 at 22:09
  • For me it had to run two commands: 1. for making symlink for files (ln -s ../data/*.*) and 2. FOr making symlink for subdirectories (ln -s ../data/*/ .) – Pranaysharma Jun 13 '22 at 12:50
4

GNU stow. https://www.gnu.org/software/stow/

Try:

# cd to dir above ./data
mkdir datalinks
stow -d datalinks data

Stow will intelligently work around existing files in the source dir as well. If a dit doesn't exist it'll symlink the dir, but if it does, it'll symlink the contents.

Takes a while to get one's head around it but once you do, it opens up a new way of thinking about file/symlink mgmt. Think about a sparsely populated tree where only new files are actual files, and everything else is a symlink to what was there before - and you have Apple's time machine. If you've ever browser a time machine backup from a shell, you'll see it's identical to an rsnapshot backup. All based on sparse tree symlink population, of which stow is the swiss army knife.

Bruce Edge
  • 1,975
  • 1
  • 23
  • 31
  • 1
    This is safer than the accepted answer, since the question does not specify if (any parts of) the sub-directory structure @guice is trying to create already exist; I believe some suggested answers would accidentally clobber parts of that existing structure with the new one. – Jason Hemann Sep 21 '22 at 13:26
  • 1
    Agreed, `stow` is an unsung hero. Doesn't get the credit it deserves. – Bruce Edge Sep 23 '22 at 01:02
2

I am answering about how to recursively sylink all files from one directory to another.

I needed this and couldn't find any good solutions around for macOS.

Following is the best solution if available:

cp -Rs <src_dir> <dest_dir>

Otherwise, what I did is:

#! /usr/bin/env bash

symlink_recursive_copy() {
    local src_dir=$1 ; shift
    local target_dir=$1
    local basename
    local cdir
    local full_path_cdir
    local full_path_target_dir
    local opwd=$PWD

    if [ -e "$target_dir" ]
    then
        >&2 echo "$target_dir already exists"
        return 1
    fi

    target_dir="$opwd/${target_dir##*/}"

    cd "$src_dir"

    while IFS= read -r -d '' file
    do
        basename="${file##*/}"
        cdir="${file%/*}"
        cd "$cdir" ; full_path_cdir=$PWD ; cd "$OLDPWD"
        mkdir -p "$target_dir/$cdir"
        ln -s "$full_path_cdir/$basename" "$target_dir/$cdir/$basename"
    done < <( find . -type f -print0 )

    cd "$opwd"
}

symlink_recursive_copy "$@"

The above should work with any kind of files containing newlines, spaces, quotes, trailing newlines etc.

Mihir Luthra
  • 6,059
  • 3
  • 14
  • 39