Tuesday, October 24, 2006

Convert Soft Links to Hard Links

Here's a Shell script I wrote to convert large numbers of soft links to hard links on Linux.
It now handles soft links on different file systems properly.

#!/bin/sh
# soft2hard.sh by Ryan Helinski
# Replace a soft (symbolic) link with a hard one.
#
# $1 is name of soft link
# Returns 0 on success, 1 otherwise
#
# Example: To replace all the soft links in a particular directory:
# find ./ -type l | tr \\n \\0 | xargs -0 -n 1 soft2hard.sh
#
# finds all files under ./ of type link (l), replaces (tr) the newline
# characters with null characters and then pipes each filename one-by-one
# to soft2hard.sh

# Check that link exists
ls "$1" > /dev/null
if test $? -eq 0
then
echo -n "File found. "
else
echo "Error: File not found."
exit 1
fi

linktarget=`find "$1" -printf "%l\0"`
linktargettype=`stat --format=%F "$linktarget"`

# Check that link target exists
if test -z "$linktarget"
then
echo "Error: Null link target, not a soft link."
exit 1
else
echo -n "Found soft link. "
fi


# Check that link target is NOT a directory
if test "$linktargettype" = "directory"
then
echo "Link to directory, skipping."
exit 0
else
echo "Not a directory."
fi

# Remove soft link
echo -n "Unlinking $1... "
unlink "$1"
if test $? -eq 0
then
echo "Done."
else
echo "Error!"
exit 1
fi

# Replace with hard link
echo "Creating hard link from $1"
echo -n " to $linktarget... "
ln "$linktarget" "$1"
if test $? -eq 0
then
echo "Done."
else
echo "Error creating hard link, replacing soft link"
ln -s "$linktarget" "$1"
exit 1
fi


exit 0

6 comments:

David said...

I was actually just looking for exactly this. From the code I would guess that upon converting a softlink, which points to a different device (or something mounted with -bind o option or similar), this would delete the symlink and then fail to recreate a hardlink for that file.

Besides that, splendid script. Thanks for saving my time. :)

Alex said...

Ditto, thanks for this timesaver!

dlo said...

I believe this line:

linktarget=`find "$1" -printf "%l\0"`

needs to be changed to:

linktarget=`find "$1" -printf "%h/%l\0"`

if you're going to use it to recurse subdirectories, as the %h preserves the path to each file.

dlo said...

I was doing something related, but different: changing absolute paths for soft links to relative paths. The links in question were all called default.jpg

Ultimately I changed the links using a one-line command:

find ./ -type l -printf "%h\0%l\0" | tr \\n \\0 | xargs -0 -n2 sh -c 'printf "%s\n" "ln -sf \"$1/`basename "$2"`\" \"$1/default.jpg\""' inline > fixlinks.sh

You can review fixlinks.sh to make sure it created what you wanted before running it with:

sh fixlinks.sh

Manuel said...

Or simpler:
cp -rL folder/ folder-copy/; rm -rf folder/; mv folder-copy folder

The -L Paramater of cp dereferences the symbolic links before copying.

Varun said...

I have few files as softlinks on linux, now how can i resue those links in windows 8 ? because in windows 8 it is not even recognising that its a softlink.

i have asked a question in SO here :
http://stackoverflow.com/questions/21403772/how-to-use-reuse-softlinks-created-on-mac-in-windows-8

but i havent got any soultion yet can you please help me ?