6

查找 java 二进制文件可能很痛苦:

  • which java/usr/bin/java
  • lh $(which java)/usr/bin/java -> /etc/alternatives/java
  • lh /etc/alternatives/java/etc/alternatives/java -> /usr/lib/jvm/jdk1.8.0_66/bin/java

有没有办法自动跟随符号链接链并打印所有成员?例如whichfollowfollow /usr/bin/java可以给出:

/usr/bin/java
-> /etc/alternatives/java
-> /usr/lib/jvm/jdk1.8.0_66/bin/java
4

4 回答 4

11

除了readlink命令之外,GNU/Linux 用户还可以使用util-linux包中的namei命令。根据其手册页:

namei 使用它的参数作为任何类型的 Unix 文件(符号链接、文件、目录等)的路径名。然后 namei 遵循每个路径名,直到找到端点(文件、目录、设备节点等)。如果它找到一个符号链接,它会显示该链接,并开始跟随它,缩进输出以显示上下文。

它的输出并不像您想要的那样漂亮,但它显示了每个路径组件,并显示它是目录、符号链接、套接字、块设备、字符设备、FIFO(命名管道)还是常规文件。

示例用法:

$ namei  /usr/bin/java

f: /usr/bin/java
 d /
 d usr
 d bin
 l java -> /etc/alternatives/java
   d /
   d etc
   d alternatives
   l java -> /usr/lib/jvm/jre-1.7.0-openjdk/bin/java
     d /
     d usr
     d lib
     d jvm
     l jre-1.7.0-openjdk -> java-1.7.0-openjdk-1.7.0.85/jre
       d java-1.7.0-openjdk-1.7.0.85
       d jre
     d bin
     - java
于 2015-10-21T11:33:46.993 回答
5

注意:(namei参见Anthony Geoghegan 的回答)和chase(参见Toby Speight 的回答)是很好的Linux选项;这个答案提供:
*跨平台解决方案* 为链的
一步 打印绝对路径,即使符号链接是用相对路径定义的。

  • 考虑typex实用程序(由我编写),它在每一步中$PATH使用绝对路径打印给定实用程序的符号链接链(typex还提供与 类似但比 更广泛的附加信息type)。
    • 最简单的安装,安装了 Node.js:npm install typex -g
    • 示例(注意如何附加使用 获得的版本信息-但是,它--version不适用于使用):java-version
        $ typex awk
        BINARY:  /usr/bin/awk@ -> /etc/alternatives/awk@ -> /usr/bin/gawk  [GNU Awk 4.0.1]
  • rreadlink是一个较低级别的实用程序(由我编写),它将符号链接链打印为任何给定文件系统路径的绝对路径。

    • 最简单的安装,安装了 Node.js:npm install rreadlink -g
    • 例子:

      $ rreadlink -1 "$(which awk)"
      /usr/bin/awk
      /etc/alternatives/awk
      /usr/bin/gawk
      
  • 下面是rreadlinkchain()一个完全符合 POSIX 的脚本/函数 - 它仅使用POSIX shell 语言功能和仅符合 POSIX 的实用程序调用。bash它是上述两个实用程序核心功能的符合 POSIX 的变体,并且非常感谢地改编自这个答案;适用于您的示例:rreadlinkchain "$(which java)"

兼容性说明:
typexrreadlink,当从 npm 注册表安装时,支持OS X 和 Linux ,但当手动安装时,它们可能也可以在带有 的 BSD 系统上运行。bash
如前所述,下面的rreadlinkchain() 函数完全符合 POSIX 标准,应该可以在大多数类 Unix 平台上运行。

#!/bin/sh

## -------
# SYNOPSIS
#   rreadlinkchain <symLink>
# DESCRIPTION
#  Recursive readlink: prints the CHAIN OF SYMLINKS from the input
#  file to its ultimate target, as ABSOLUTE paths, with each path on a separate
#  line.
#  Only the ultimate target's path is canonical, though.
#  A broken symlink in the chain causes an error that reports the
#  non-existent target.
#  An input path that is not a symlink will print its own canonical path.
# LIMITATIONS
#   - Won't work with filenames with embedded newlines or filenames containing 
#     the string ' -> '.
# COMPATIBILITY
#   Fully POSIX-compliant.
# EXAMPLES
#     # Print the symlink chain of the `git` executable in the $PATH.
#   rreadlinkchain  "$(which git)"
#    # Ditto, using single-line `ls -l`-style format ('a@ -> b')
#   rreadlinkchain  "$(which git)" | sed -nE -e '$!{a\'$'\n''@ -> ' -e '}; p' | tr -d '\n'
# THANKS
#   https://stackoverflow.com/a/1116890/45375
rreadlinkchain() ( # execute in *subshell* to localize the effect of `cd`, ...

  target=$1 targetDir= targetName= CDPATH= 

  # Try to make the execution environment as predictable as possible:
  # All commands below are invoked via `command`, so we must make sure that
  # `command` itself is not redefined as an alias or shell function.
  # (Note that command is too inconsistent across shells, so we don't use it.)
  # `command` is a *builtin* in bash, dash, ksh, zsh, and some platforms do not 
  # even have an external utility version of it (e.g, Ubuntu).
  # `command` bypasses aliases and shell functions and also finds builtins 
  # in bash, dash, and ksh. In zsh, option POSIX_BUILTINS must be turned on for
  # that to happen.
  { \unalias command; \unset -f command; } >/dev/null 2>&1
  [ -n "$ZSH_VERSION" ] && options[POSIX_BUILTINS]=on # make zsh find *builtins* with `command` too.

  while :; do
      # Unless the file is a symlink OR exists, we report an error - note that using `-e` with a symlink reports the *target*'s existence, not the symlink's.
    [ -L "$target" ] || [ -e "$target" ] || { command printf '%s\n' "ERROR: '$target' does not exist." 1>&2; return 1; }
      # !! We use `cd` to change to the target's folder
      # !! so we can correctly resolve the full dir. path.
    command cd "$(command dirname -- "$target")" # note: cd "" is the same as cd . - i.e., a no-op.
    targetDir=$PWD
    targetName=$(command basename -- "$target")
    [ "$targetName" = '/' ] && targetName='' # !! curiously, `basename /` returns '/'
    done=0
    if [ ! -L "$targetName" ]; then
        # We've found the ultimate target (or the input file wasn't a symlink to begin with).
        # For the *ultimate* target we want use `pwd -P` to make sure we use the actual, physical directory,
        # (not a symlink) to get the *canonical* path.
      targetDir=$(command pwd -P)
      done=1
    fi
      # Print (next) path - note that we manually resolve paths ending 
      # in /. and /.. to make sure we have a normalized path.
    if [ "$targetName" = '.' ]; then
      command printf '%s\n' "${targetDir%/}"
    elif  [ "$targetName" = '..' ]; then
      # Caveat: something like /var/.. will resolve to /private (assuming
      # /var@ -> /private/var), i.e. the '..' is applied AFTER canonicalization.
      command printf '%s\n' "$(command dirname -- "${targetDir}")"
    else
      command printf '%s\n' "${targetDir%/}/$targetName"
    fi
      # Exit, if we've hit the non-symlink at the end of the chain.
    [ "$done" = 1 ] && break 
    # File is symlink -> continue to resolve.
    # Parse `ls -l` output, which, unfortunately, is the only POSIX-compliant
    # way to determine a symlink's target. Hypothetically, this can break with
    # filenames containig literal ' -> ' and embedded newlines.
    target=$(command ls -l -- "$targetName")
    target=${target#* -> }
  done
)

rreadlinkchain "$@"
于 2015-10-21T11:58:05.163 回答
2

考虑安装chase

示例输出:

$ chase --verbose /usr/bin/java
/usr/bin/java
-> /etc/alternatives/java
-> /usr/lib/jvm/java-7-openjdk-amd64/jre/bin/java
/usr/lib/jvm/java-7-openjdk-amd64/jre/bin/java

包装说明:

:追逐
状态:已安装
自动安装:无
版本:0.5.2-4build2
优先级:可选
部分:universe / utils
维护者:Ubuntu开发者
架构:amd64
未压缩大小:61.4 k
取决于:libc6(> = 2.3.4),libgc1c2( >= 1:7.2d)
冲突:追逐
描述:跟随符号链接并打印出它的目标文件
Chase 是一个小型实用程序,用于跟踪符号链接指向的实际文件 - 如果您愿意,可以跟踪符号链接。成功运行的结果保证是一个不是符号链接的现有文件。

于 2015-10-21T12:25:14.883 回答
1

您可以使用readlink循环while。下面的功能将起作用:

function follow {
  path="$1"
  echo "$path"
  while path=$(readlink "$path"); do
    echo "-> $path"
  done
}

follow "/usr/bin/java"
于 2015-10-21T09:39:28.837 回答