如何在bash中查找指向符号链接的符号链接
我正在准备一个bash脚本来验证符号链接的正确性。我想在以下情况下进行诊断:如何在bash中查找指向符号链接的符号链接,bash,Bash,我正在准备一个bash脚本来验证符号链接的正确性。我想在以下情况下进行诊断: @符号链接已断开 @symlink指向另一个@symlink--(用symlinks链的最终目标修复) @symlink指向另一个@symlink,它已断开 @符号链是一个循环 当symlink指向symlink时,我在诊断第2)点时遇到了很大的问题 我试图使用readlink,但它返回符号链接链的最终目标,而不是指向的符号链接名称。我尝试在没有-f参数的情况下运行它,但没有任何帮助。然后与find的组合给了我糟糕的结
-f
参数的情况下运行它,但没有任何帮助。然后与find的组合给了我糟糕的结果…有人能帮我解决这个问题吗 下面我将代码粘贴到当前版本中
failed=0
for file in path/*
do
if [[ -L "$file" ]]
then
if [[ ! -a "$file" ]]
then
echo "Symlink '$file' is broken -- target object doesn't exists."
failed=1
elif [[ -L "$(readlink -f $file)" ]]
then
echo "Symlink '$file' points to another symlink: '$(readlink $file)'"
failed=1
fi
fi
done
exit $failed
更新
测试文件结构(其中讨论了bash脚本中的symlinks.sh):
从您的问题描述中不清楚如何处理传递链末端的断开链接。我将简单地报告一个错误并继续
#!/bin/sh
rc=0
for file in path/*; do
if [ -L "$file" ]; then
nfile=$file
while [ -L "$nfile" ]; do
nfile=$(cd $(dirname "$nfile"); abspath $(readlink "$nfile"))
done
if ! [ -r "$nfile" ]; then
echo "$0: $file eventually resolves to nonexistent target $nfile" >&2
rc=1
else
# FIXME: maybe avoid doing this needlessly?
rm "$file"
ln -s "$nfile" "$file"
fi
fi
done
exit "$rc"
我是否正确理解您希望用指向最终目标的符号链接替换指向符号链接(递归等)的符号链接?这个脚本做到了这一点,尽管有点过分,因为它将重写所有解析的符号链接;优化这一点以避免不必要的操作只是一个练习。我不是bash方面的专家,因此可能有人向我们展示了更简单的解决方案,但这次我分享了我的脚本(它确保符号链接的相对路径与相对链接):
挑剔:你所有的分号都是无用的,可以删除而不会产生不良影响。(如果您在同一行中编写了
if…;那么,您才需要它们。)感谢您的提示:)当我试图在我的测试文件结构(问题中的更新部分)上运行您的脚本时,我得到了:symlinks.sh:symlinks/sym_break最终解析为不存在的目标../noexist(…)以及symlinks目录中所有6个symlinks的类似结果,当它应该只出现在那些2与“破”关键字。你知道为什么它不能按预期工作吗?注:当然,我将路径
目录从您的代码更改为符号链接
,以及关于链末端断开链接的处理-您将其报告为错误的决定对我来说是完美的:)请参阅更新的答案。错误出现在相对符号链接中——原始代码将它们解释为相对于当前目录,而正确的解决方案是相对于我们正在解析的符号链接的目录解析它们。我还修复了用更新的链接覆盖指向目录的符号链接的错误。如果您需要的话,它为OS X提供了一个可移植的abspath
功能。非常感谢:)您的代码和建议非常有用:)我尝试在完成后发布此脚本的扩展版本。如果能阅读你的提示和评论,那就太好了。最佳:)重复的local
声明是多余的。您应该只声明一次变量local
,通常在函数的开头。
#!/bin/sh
# The task is to create bash script, which validates symlinks and reports when:
# (1) - symlink is broken
# (2) - symlink target point is broken
# (3) - symlink points to another symlink
# (4) - symlinks chain is a cycle
FAILED=0
FIX_SYMLINKS=false
DIR_UP="../"
function file_under_symlink_absolute_path {
local file_under_symlink="$(readlink $1)"
local file_dirname="$(dirname $1)"
if [[ "$file_under_symlink" == *"$DIR_UP"* ]]; then
if [[ "$file_under_symlink" == *"$DIR_UP$DIR_UP"* ]]; then
while [[ "$file_under_symlink" == *"$DIR_UP"* ]]; do
local file_under_symlink="${file_under_symlink#$DIR_UP}"
if [[ "$file_dirname" == *"/"* ]]; then
local file_dirname="${file_dirname%/*}"
else
local file_dirname=""
fi
done
if [[ "$file_dirname" != "" ]]; then
local file_dirname="$file_dirname/$file_under_symlink"
else
local file_dirname="$file_under_symlink"
fi
else
if [[ "$file_dirname" == *"/"* ]]; then
local file_dirname="${file_dirname%/*}/${file_under_symlink#$DIR_UP}"
else
local file_dirname="${file_under_symlink#$DIR_UP}"
fi
fi
else
local file_dirname="$file_dirname/$file_under_symlink"
fi
echo "$(pwd)/$file_dirname"
}
function file_under_symlink_relative_path {
local file_dirname="$(dirname $1)"
local symlink_target="$2"
local file_under_symlink="$3"
if [[ "$symlink_target" == *"$file_dirname"* ]]; then
local prefix2cut="$(pwd)/$file_dirname"
local target="${symlink_target##$prefix2cut}"
else
local symlink_target_dirname="$(dirname $symlink_target)"
local symlink_target_basename="$(basename $symlink_target)"
if [[ "$file_under_symlink" == "$symlink_target_dirname"* ]]; then
local level_diff="${file_under_symlink#$symlink_target_dirname/}"
local target="$symlink_target_basename"
while [[ "$level_diff" == *"/"* ]]; do
local level_diff="${level_diff%/*}"
local target="$DIR_UP$target"
done
else
if [[ "$file_dirname" == *"/"* ]]; then
local prefix2cut="$(pwd)/${file_dirname%/*}/"
else
local prefix2cut="$(pwd)/"
fi
local target="$DIR_UP${symlink_target#$prefix2cut}"
fi
fi
echo "$target"
}
function valid_symlinks {
local current_dir="$1"
for file in "$current_dir"/*; do
if [[ -d "$file" ]]; then
valid_symlinks "$file"
elif [[ -L "$file" ]]; then
local symlink_target=$(readlink -e "$file")
local file_under_symlink_abs="$(file_under_symlink_absolute_path $file)"
# reports (1), (2), (4)
if [[ ! -a "$file" ]]; then
echo "BROKEN Symlink '$file' is broken, target object doesn't exists."
FAILED=1
# reports (3)
# it happends when file under symlink is not the symlink target file
elif [[ "$file_under_symlink_abs" != "$symlink_target" ]]; then
if $FIX_SYMLINKS && [[ -r "$symlink_target" ]]; then
local target="$(file_under_symlink_relative_path $file $symlink_target $file_under_symlink_abs)"
echo "Symlink '$file' points to another symlink. It will be replace with direct symlink to target '$target'."
ln -fs "$target" "$file"
else
local target="${file_under_symlink_abs#$(pwd)/}"
echo "Symlink '$file' points to another symlink '$target'."
FAILED=1
fi
fi
fi
done
}
if [[ -d "$1" ]]; then
start_point=${1#$(pwd)/}
start_point=${start_point%/}
valid_symlinks "$start_point"
else
echo "ERROR: You have to specify the start location path."
FAILED=1
fi
exit $FAILED