1

I have the following folder structure:

.
`-- top_level/
    |-- sub-01_ses-01/
    |   `-- filtered_data.tar.gz*
    |-- sub-01_ses-02/
    |   `-- filtered_data.tar.gz*
    |-- sub-02_ses-01/
    |   `-- filtered_data.tar.gz*
    |-- sub-02_ses-02/
    |   `-- filtered_data.tar.gz*

I wanted to create symbolic links to these files preserving the parent structure (since they all have the same filenames). Here's what I tried:

find -name "filtered_data.tar.gz" \
     -exec cp -s --parents --no-clobber -t /home/data/filtered {} \;

Now, I notice that cp does create the parent structure, but the symbolic links fail and I get the following notice:

cp: '/home/data/filtered/./sub-01_ses-01/filtered_data.tar.gz': can make relative symbolic links only in current directory

I'd like to understand why this is hapenning, and what the cp warning is trying to tell me. Also, any pointers on how to fix the issue would be greatly appreciated.

Daniel
  • 11,332
  • 9
  • 44
  • 72

1 Answers1

1

Found the solution here: symlink-copying a directory hierarchy

The path of the file to cp must be absolute, not ./something. So, this should work for you:

find $(pwd) -name "filtered_data.tar.gz" \
     -exec cp -s --parents --no-clobber -t /home/data/filtered {} \;

Per your comment about what you're really trying to do, here's a Python script that does it. You should be able to tweak it.

#!/usr/bin/env python3

import os

target_filename = 'filtered_data.tar.gz'
top_src_dir = '.'
top_dest_dir = 'dest'

# Walk the source directory recursively looking for
# target_filename
for parent, dirs, files in os.walk(top_src_dir):
    # debugging
    # print(parent, dirs, files)

    # Skip this directory if target_filename not found
    if target_filename not in files:
        continue

    # Strip off all path parts except the immediate parent
    local_parent = os.path.split(parent)[-1]
    # Compute the full, relative path to the symlink
    dest_file = os.path.join(top_dest_dir, local_parent, target_filename)

    # debugging
    # print('{} {}'.format(dest_file, os.path.exists(dest_file)))

    # Nothing to do if it already exists
    if os.path.exists(dest_file):
        print('{} already exists'.format(dest_file))
        continue

    # Make sure the destination path exists
    dest_dir = os.path.dirname(dest_file)
    os.makedirs(dest_dir, exist_ok=True)

    # Translate the relative path to target_filename
    # to be relative based on the new destination dir
    src_file = os.path.join(parent, target_filename)
    src_file = os.path.relpath(src_file, start=dest_dir)

    os.symlink(src_file, dest_file)
    print('{} --> {}'.format(dest_file, src_file))
Harvey
  • 5,703
  • 1
  • 32
  • 41
  • Thanks, this works indeed, but then I get **the whole parent structure**!. I only wanted the structure starting from `sub-XX_ses-XX` – Daniel Jun 21 '17 at 19:49
  • 1
    It doesn't look like `cp` supports that. You'll probably have to write a script or a program (Python would be easy) to do this. – Harvey Jun 21 '17 at 20:55