如何在bash中找到指向符号链接的符号链接

时间:2015-12-11 10:50:41

标签: bash

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

  1. @symlink坏了
  2. @symlink指向另一个@symlink - (用symlinks链的最终目标修复它)
  3. @symlink指向另一个@symlink,这是破碎的
  4. @symlinks chain是一个循环
  5. 在诊断符号链接指向符号链接时,第2点我遇到了大问题。

    我尝试使用 readlink ,但它返回符号链接的最终目标,而不是指向符号链接名称。我尝试在没有-f参数的情况下运行它,但它没有帮助。然后与发现的组合给了我糟糕的结果......
    任何人都可以帮我解决这个问题吗?

    下面我将代码粘贴到当前版本中。

    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脚本):

    **hook-tests**

2 个答案:

答案 0 :(得分: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"

我是否正确理解您希望将符号链接(递归等)的符号链接替换为最终目标的符号链接?这个脚本可以做到这一点,虽然有些过分,但它会重写所有解析的符号链接;优化这一点,以避免不必要地做它是一个练习。

答案 1 :(得分:0)

我不是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