Take the 2-minute tour ×
Server Fault is a question and answer site for professional system and network administrators. It's 100% free, no registration required.

How can you replace all symbolic-links in a directory (and children) with their targets on Mac OS X? If the target is not available, I'd prefer to leave the soft link alone.

share|improve this question

3 Answers 3

up vote 1 down vote accepted

If you are using mac OSX aliases, then find . -type l won't come up with anything.

You can use the following [Node.js] script to move/copy the targets of your symlinks to another directory:

fs = require('fs')
path = require('path')

sourcePath = 'the path that contains the symlinks'
targetPath = 'the path that contains the targets'
outPath = 'the path that you want the targets to be moved to'

fs.readdir sourcePath, (err,sourceFiles) ->
    throw err if err

    fs.readdir targetPath, (err,targetFiles) ->
        throw err if err

        for sourceFile in sourceFiles
            if sourceFile in targetFiles
                targetFilePath = path.join(targetPath,sourceFile)
                outFilePath = path.join(outPath,sourceFile)

                console.log """
                    Moving: #{targetFilePath}
                        to: #{outFilePath}
                    """
                fs.renameSync(targetFilePath,outFilePath)

                # if you don't want them oved, you can use fs.cpSync instead
share|improve this answer

Here are versions of chmeee's answer that uses readlink and will work properly if there are spaces in any filename:

New filename equals old link name:

find . -type l | while read -r link
do 
    target=$(readlink "$link")
    if [ -e "$target" ]
    then
        rm "$link" && cp "$target" "$link" || echo "ERROR: Unable to change $link to $target"
    else
        # remove the ": # " from the following line to enable the error message
        : # echo "ERROR: Broken symlink"
    fi
done

New filename equals target name:

find . -type l | while read -r link
do
    target=$(readlink "$link")
    # using readlink here along with the extra test in the if prevents
    # attempts to copy files on top of themselves
    new=$(readlink -f "$(dirname "$link")/$(basename "$target")")
    if [ -e "$target" -a "$new" != "$target" ]
    then
        rm "$link" && cp "$target" "$new" || echo "ERROR: Unable to change $link to $new"
    else
        # remove the ": # " from the following line to enable the error message
        : # echo "ERROR: Broken symlink or destination file already exists"
    fi
done
share|improve this answer
    
How do you use this? I can't get it to work... –  balupton Apr 22 '12 at 13:43
    
@balupton: Run it in a directory that includes symlinks in it or its subdirectories. "I can't get it to work" tells me nothing at all. What did you try? What did you expect the outcome to be? How did the actual outcome differ from you expectations? What error messages did you receive? What is the environment (OS, shell, etc.)? –  Dennis Williamson Apr 22 '12 at 15:27
    
Seems my problem is that I have OSX Aliases instead of symlinks: stackoverflow.com/questions/5084584/… - so find . -type l doesn't work... Haven't found a solution yet... –  balupton Apr 22 '12 at 15:51

You didn't say what names the files should have after the replacement.

This script considers the replaced links should have the same names they had as links.

for link in `find . -type l`
do 
  target=`\ls -ld $link | sed 's/^.* -> \(.*\)/\1/'`
  test -e "$target" && (rm "$link"; cp "$target" "$link")
done

If you want the files to have the same name as the target, this should do.

for link in `find . -type l`
do
  target=`\ls -ld $link | sed 's/^.* -> \(.*\)/\1/'`
  test -e "$target" && (rm $link; cp "$target" `dirname "$link"`/`basename "$target"`)
done
share|improve this answer

Your Answer

 
discard

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.