Working with symbolic links on Unix-like systems can sometimes lead to circular references, especially in complex directory structures. On HP-UX systems, these circular symlinks can cause various issues from infinite loops in scripts to system performance degradation.
The command you're using:
ls -lrt find ./ -follow -type l
has several limitations:
- The
-follow
option in find stops at the first circular reference - Command substitution (backticks) evaluates before the full path is resolved
- This approach doesn't systematically detect all circular references
Here are two robust approaches to identify circular symlinks:
Method 1: Using find with depth tracking
find /path/to/search -type l -exec sh -c '
for link; do
target=$(readlink "$link")
[ -e "$target" ] || continue
depth=0
while [ -L "$target" ] && [ $depth -lt 20 ]; do
target=$(readlink "$target")
if [ "$target" = "$link" ]; then
echo "Circular symlink detected: $link"
break
fi
depth=$((depth + 1))
done
done
' sh {} +
Method 2: Perl script for comprehensive checking
#!/usr/bin/perl
use strict;
use warnings;
use Cwd 'abs_path';
my $dir = shift || '.';
find_symlinks($dir);
sub find_symlinks {
my $path = shift;
if (-l $path) {
my $target = readlink($path);
my $abs_path = abs_path($path);
my $abs_target = abs_path($target);
if (defined $abs_path && defined $abs_target && $abs_path eq $abs_target) {
print "Circular symlink: $path -> $target\n";
}
}
return if !-d $path;
opendir(my $dh, $path) or return;
while (my $entry = readdir($dh)) {
next if $entry eq '.' || $entry eq '..';
find_symlinks("$path/$entry");
}
closedir $dh;
}
For systems with thousands of symlinks, consider these optimizations:
- Run checks during low-usage periods
- Limit scope to specific directories when possible
- Cache results for repeated analysis
To avoid circular symlinks:
- Implement a symlink creation policy
- Use relative paths carefully
- Regularly audit symlink structures
Circular symbolic links occur when a symlink points to itself either directly or through a chain of other symlinks. These can cause infinite loops during file operations and should be identified and resolved.
The command you tried:
ls -lrt find ./ -follow -type l
fails because the backtick expansion happens before the find command executes, and -follow
stops at the first symlink it encounters.
Here are three reliable approaches:
Method 1: Using find with -exec and readlink
find /path/to/search -type l -exec sh -c '
for link; do
target=$(readlink "$link")
if [ "$target" = "$link" ]; then
echo "Circular symlink found: $link"
fi
done
' sh {} +
Method 2: Recursive Check with Depth Limit
check_symlink() {
local file=$1 depth=${2:-0}
[ $depth -gt 20 ] && { echo "Possible circularity: $file"; return; }
if [ -L "$file" ]; then
local target=$(readlink "$file")
if [ -e "$target" ]; then
check_symlink "$target" $((depth+1))
fi
fi
}
export -f check_symlink
find /path/to/search -type l -exec bash -c 'check_symlink "$0"' {} \;
Method 3: HP-UX Specific Solution
For HP-UX systems, you might need to use:
find / -type l -exec ls -ld {} \; 2>/dev/null | awk '{
split($NF,a," -> ");
if(a[1] == a[2]) print "Circular link: " a[1]
}'
For complex cases where symlinks form circular chains:
#!/usr/bin/ksh
visited=()
check_link() {
local path=$1
local -i depth=$2
if (( depth > 20 )); then
echo "Circularity detected in path: ${visited[*]}"
return 1
fi
if [[ -L $path ]]; then
local target=$(command ls -ld "$path" 2>/dev/null | awk '{print $NF}')
if [[ " ${visited[@]} " =~ " $target " ]]; then
echo "Circular symlink chain found:"
printf " %s\n" "${visited[@]}"
echo " $target"
return 1
fi
visited+=("$target")
check_link "$target" $((depth + 1))
fi
}
export -f check_link
find /path/to/check -type l -exec ksh -c 'check_link "$0" 0' {} \;
- Always use absolute paths for symlink targets
- Implement regular symlink audits in cron jobs
- Consider using hard links instead of symlinks when possible
- Document all intentional symlinks in a central registry