Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/shell/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Shell 如何在没有readlink或realpath的情况下递归解析符号链接?_Shell_Posix_Sh - Fatal编程技术网

Shell 如何在没有readlink或realpath的情况下递归解析符号链接?

Shell 如何在没有readlink或realpath的情况下递归解析符号链接?,shell,posix,sh,Shell,Posix,Sh,如果readlink和realpath不可用,那么脚本查找链接目标的最佳可移植(POSIX?)方法是什么 您是否愿意ls-l,如果它以l开头,请使用sed将->后面的文本重复,直到它不再以l开头?限制:-printf不是POSIX指定的选项 #!/bin/bash tmp=<symlink-name> tmp1='' while tmp=$(find "$tmp" -prune -printf "%l" 2>/dev/null); do target="$tmp1"

如果
readlink
realpath
不可用,那么脚本查找链接目标的最佳可移植(POSIX?)方法是什么


您是否愿意
ls-l
,如果它以
l
开头,请使用
sed
->
后面的文本重复,直到它不再以
l
开头?

限制:
-printf
不是POSIX指定的选项

#!/bin/bash

tmp=<symlink-name>
tmp1=''
while tmp=$(find "$tmp" -prune -printf "%l" 2>/dev/null); do
    target="$tmp1" && tmp1="$tmp"
done;
echo "$target"
#/bin/bash
tmp=
tmp1=''
而tmp=$(查找“$tmp”-prune-printf“%l”2>/dev/null);做
target=“$tmp1”和&tmp1=“$tmp”
完成;
回显“$target”
Per(也支持GNU查找方法):

一个广泛使用的选项(虽然不是纯POSIX)是使用
perl

target=/path/to/symlink-name perl -le 'print readlink $ENV{target}'
如果保证符号链接的名称不包含
->
,则可以解析
ls
的输出


以下代码结合了两种方法:

# define the best readlink function available for this platform
if command -v readlink >/dev/null 2>/dev/null; then
  # first choice: Use the real readlink command
  readlink() {
    command readlink -- "$@"
  }
elif find . -maxdepth 0 -printf '%l' >/dev/null 2>/dev/null; then
  # second choice: use GNU find
  readlink() {
    local ll candidate >/dev/null 2>&1 ||:
    if candidate=$(find "$1" -maxdepth 0 -printf '%l') && [ "$candidate" ]; then
      printf '%s\n' "$candidate"
    else
      printf '%s\n' "$1"
    fi
  }
elif command -v perl >/dev/null 2>/dev/null; then
  # third choice: use perl
  readlink() {
    local candidate ||:
    candidate=$(target=$1 perl -le 'print readlink $ENV{target}')
    if [ "$candidate" ]; then
      printf '%s\n' "$candidate"
    else
      printf '%s\n' "$1"
    fi
  }
else
  # fourth choice: parse ls -ld
  readlink() {
    local ll candidate >/dev/null 2>&1 ||:
    ll=$(LC_ALL=C ls -ld -- "$1" 2>/dev/null)
    candidate=${ll#* -> }
    if [ "$candidate" = "$ll" ]; then
      printf '%s\n' "$1"
    else
      printf '%s\n' "$candidate"
    fi
  }
fi

readlink_recursive() {
    local path prev_path oldwd found_recursion >/dev/null 2>&1 ||:
    oldwd=$PWD; path=$1; found_recursion=0

    while [ -L "$path" ] && [ "$found_recursion" = 0 ]; do
        if [ "$path" != "${path%/*}" ]; then
          cd -- "${path%/*}" || {
            cd -- "$oldwd" ||:
            echo "ERROR: Directory '${path%/*}' does not exist in '$PWD'" >&2
            return 1
          }
          path=${PWD}/${path##*/}
        fi
        path=$(readlink "$path")
        if [ -d "$path" ]; then
          cd -- "$path"
          path=$PWD
          break
        fi
        if [ "$path" != "${path%/*}" ]; then
          cd -- "${path%/*}" || {
            echo "ERROR: Could not traverse from $PWD to ${path%/*}" >&2
            return 1
          }
          path=${PWD}/${path##*/}
        elif [ "$PWD" != "$oldwd" ]; then
          path=${PWD}/$path
        fi
        for prev_path; do
          if [ "$path" = "$prev_path" ]; then
            found_recursion=1
            break
          fi
        done
        set -- "$path" "$@" # record path for recursion check
    done

    if [ "$path" != "${path%/../*}" ]; then
      cd "${path%/*}" || {
        echo "ERROR: Directory '${path%/*}' does not exist in $PWD" >&2
        return 1
      }
      printf '%s\n' "$PWD/${path##*/}"
    else
      printf '%s\n' "$path"
    fi
    cd -- "$oldwd" ||:
}

这是另一个与Charles Duffy非常相似的解决方案。我没有那么丰富的经验,因此可能存在一些非POSIX或性能问题。我是在看了Charles的解决方案并替换了任何我不理解的内容后得出这个结论的:-P很可能在这里的任何问题得到解决后,你会再次得到Charles的解决方案

resolve() {
    local arg path absolute ll dir prev_path oldwd found_recursion base >/dev/null 2>&1 ||:
    arg="$1"; path="$1"; oldwd=$PWD; found_recursion=0
    dir=$(dirname "$path")
    cd -- "$dir" || {
        cd -- "$oldwd" ||:
        echo "While resolving '$arg' could not go to '$dir'" >&2
        return 1
    }
    if [ $PWD = "/" ]; then
        absolute="/$(basename $path)"
    else
        absolute="$PWD/$(basename $path)"
    fi
    [ "$path" != "$absolute" ] && set -- "$absolute"
    while [ -L "$absolute" ] && [ "$found_recursion" = 0 ]; do
        ll=$(LC_ALL=C \ls -ld -- "$absolute" 2>/dev/null)
        path=${ll#* -> }
        dir=$(dirname "$path")
        cd -- "$dir" || {
            cd -- "$oldwd" ||:
            echo "While resolving '$arg' could not go to '$dir'" >&2
            return 1
        }
        base=$(basename "$path")
        absolute="$PWD/$base"
        for prev_path; do
            if [ "$absolute" = "$prev_path" ]; then
                found_recursion=1
                break
            fi
        done
        set -- "$absolute" "$@"
    done
    if [ -d "$absolute" ]; then
        cd -- "$absolute" || {
            cd -- "$oldwd" ||:
            echo "While resolving '$arg' could not go to '$absolute'" >&2
            return 1
        }
        printf '%s\n' "$PWD"
    else
        printf '%s\n' "$absolute"
    fi
}

编辑:现在使用
$PWD
printf
,规范化结果if目录。

@Charles Duffy:
-maxdepth
替换为
-prune
。我留下这个答案只是为了鼓舞人心,标题中有大胆的限制。对于GNU工具可用的系统来说,这是一个很好的答案——正如我在标题中指出的,这是BashFAQ认可的方法之一。如果
--
出现错误,则必须删除
--
。退出。以破折号开头的文件名将不起作用(如果前面没有目录名)。查找它--实际上,在POSIX规范的当前版本中,支持
--
是必须的。请参阅中实用程序语法指南的指南10,我对其进行了相当大的扩展,以正确处理更改目录的相对符号链接。不客气。:)
||:
告诉shell即使失败也不要将该行视为错误(如果使用了
set-e
,则阻止自动退出),从而允许它在支持该功能时声明东西
local
,否则忽略该行。大多数
/bin/sh
实现支持
本地
,但POSIX标准并不要求它们。顺便说一句,
| |:
只是
| true
的常用习惯用法
true
的同义词(字面上,在大多数shell中调用完全相同的内置命令)。
$(pwd)
的效率远远低于
$pwd
,因为与任何
$(…)
扩展一样,它分叉子shell,在该子shell中运行操作,读取其stdout,等待它退出,并替换该输出。此外,在引用时也存在错误<当
path
包含空格时,code>$(basename$path)会严重中断。至少使用
$(basename“$path”)
,尽管这仍然比等效的参数扩展效率低得多。此外,不寻常地使用
echo
是一种糟糕的形式——echo的功能因平台而异。请参阅:某些版本的
echo
(POSIX+XSI)默认情况下展开反斜杠展开;在某些情况下,反斜杠展开的行为尚未定义;处理仅包含
“-n”
的字符串时的行为未定义;字符串
-e
的处理是标准定义的(一致性实现必须在其输出上打印
-e
),但包括bash在内的一些实现在这方面不符合标准。否则,这并不可怕。因为它总是使用
ls-ld
,而不是在可用时更喜欢GNU find或perl,所以它无法正确处理名称中包含“->”的符号链接,并且链接目标中包含不可打印字符的行为可能会被破坏,但如果您只是尝试处理行为良好的文件名(幸运的是!),这可能是可以接受的。说到性能——在定义函数之前,我已经更新了我的版本,只运行了一次测试来确定可用的最佳readlink实现,而不是每次调用readlink时都这样做。