【问题标题】:How to find symlinks pointing to symlinks in bash如何在bash中找到指向符号链接的符号链接
【发布时间】:2016-03-17 06:15:48
【问题描述】:

我正在准备一个 bash 脚本来验证符号链接的正确性。我想诊断什么时候:

  1. @symlink 已损坏
  2. @symlink 指向另一个@symlink --(用符号链接链的最终目标修复它)
  3. @symlink 指向另一个已损坏的@symlink
  4. @symlinks 链是一个循环

在诊断符号链接指向符号链接时,我对第 2 点有很大的问题。

我尝试使用 readlink,但它返回符号链接链的最终目标,而不是指向符号链接名称。我试图在没有-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

更新

测试文件结构(其中 symlinks.sh 是讨论 bash 脚本):

【问题讨论】:

  • 挑剔:你所有的分号都是无用的,可以删除而不会产生不良影响。 (只有在同一行写 if ...; then 时才需要它们。)
  • 感谢您的提示 :)

标签: bash


【解决方案1】:

从您的问题描述中不清楚应如何处理传递链末端的断开链接。我会简单地报告一个错误并继续前进。

#!/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"

我是否正确理解您希望将符号链接(等递归)的符号链接替换为最终目标的符号链接?这个脚本可以做到这一点,尽管它会重写所有解析的符号链接。对此进行优化以避免不必要的操作留作练习。

【讨论】:

  • 当我尝试在我的测试文件结构上运行您的脚本(我的问题中的更新部分)时,我得到:symlinks.sh: symlinks/sym_broken 最终解析为不存在的目标 ../noexist ( ...) 和 symlinks 目录中的所有 6 个符号链接类似,当它应该只出现在那些带有“破碎”关键字的 2 个时。你有什么想法为什么它不能按预期工作吗? PS。当然,我将 path 目录从您的代码更改为 symlinks
  • 关于在链末端处理损坏的链接 - 您决定将其报告为 en 错误对我来说是完美的:)
  • 查看更新的答案。该错误存在于相对符号链接中——原始代码将它们解释为相对于当前目录,而正确的解决方案是相对于我们正在解析的符号链接的目录来解析它们。我还修复了用更新的链接覆盖目录的符号链接的错误。 stackoverflow.com/a/3572105/874188 具有适用于 OS X 的便携式 abspath 功能,如果您需要的话。
  • 非常感谢 :) 您的代码和建议非常有帮助 :) 我尝试在完成此脚本后发布我的扩展版本。很高兴阅读您的提示和 cmets。最好的:)
【解决方案2】:

我不是 bash 方面的专家,因此可能有人向我们展示了更简单的解决方案,但这次我分享了我的脚本(它确保了具有相对链接的符号链接的相对路径):

#!/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

【讨论】:

  • 重复的local 声明是多余的。您应该只声​​明一次变量local,通常在函数的开头。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-04
  • 2021-12-19
  • 2018-03-31
  • 1970-01-01
  • 2015-12-23
  • 1970-01-01
相关资源
最近更新 更多