Shell one-liners for manipulating symlinks
Showing symlink targets
I was trying to clean up a directory filled with files and symlinks. Many of
the symlinks' targets did not exist anymore (old package versions). I wrote a
few one-liners to help me examine and clean up the directory. Here's the first
one-liner. find . -type l -print0 | sed -e 's/\.\///g;' | xargs -0 -n1 |
while read line; do canon=$( readlink -f "${line}" 2>/dev/null ); test -e
"${canon}" && echo "${line} is $( /bin/ls -d --color=always "${canon}" )" ||
echo "INVALID ${line}"; done
In expanded form: find . -type l -print0 | \
sed -e 's/\.\///g;' | \ xargs -0 -n1 | \ while read line; do \ canon=$(
readlink -f "${line}" 2>/dev/null ); test -e "${canon}" && echo "${line} is $(
/bin/ls -d --color=always "${canon}" )" || echo "INVALID ${line}"; done
The
find command lists symlinks in the current directory, with special null
characters as the separators. This will allow correct operation with spaces in
filenames. You don't normally encounter this in GNU/Linux, but you never know!
The sed removes the dot slash ./ from the beginning of filenames. I just don't
like seeing that. If I had selected find * earlier, I wouldn't need the sed
command, but I would miss any files whose names begin with dot (e.g.,
.gitignore) and I would still find file . which I would have to exclude
somehow anyway (well, not here because I'm searching for symlinks, but I
personally never try to run find * no matter what). xargs -0 -n1 now removes
the null character separators, and feeds each individual entry one at a time
to the next command. While: So for each entry, read the symlink. If the target
is a file, display it in whatever color ls would show it (without entering
directories), because colors are pretty! Otherwise, print the line with
"INVALID" in front of it.
Cleaning up the invalid symlinks
find . -type l -print0 | sed -e 's/\.\///g;' | xargs -0 -n1 | while read
line; do canon=$( readlink -f "${line}" 2>/dev/null ); test -e "${canon}" &&
true || { echo /bin/rm "./${line}"; }; done
Replace the echo commands with
true for the affirmative and the echo /bin/rm command. To actually effect the
change, remove the echo in front of /bin/rm.
Comments