Как составить список всех участников символической цепочки?

Поиск 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

Есть ли способ автоматически следовать цепочке символических ссылок и печатать все элементы? например whichfollow или же follow /usr/bin/java мог бы дать:

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

4 ответа

В добавок к readlink пользователи GNU/Linux могут использовать namei команда из пакета util-linux. Согласно его справочной странице:

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

Замечания: namei (см . ответ Энтони Геогегана) и chase (см . ответ Тоби Спейта) отличные варианты 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 и только POSIX-совместимые вызовы утилит. Это POSIX-совместимый вариант bash функция в основе двух утилит выше, и был с благодарностью адаптирован из этого ответа; применяется к вашему примеру: rreadlinkchain "$(which java)"

Примечания совместимости:
typex а также rreadlink , при установке из реестра 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://stackru.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 "$@"

Рассмотреть возможность установки 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
Приоритет: необязательно
Раздел: Вселенная / Утилиты
Сопровождающий: разработчики Ubuntu
Архитектура: amd64
Несжатый размер: 61,4 k
Зависит от: libc6 (>= 2.3.4), libgc1c2 (>= 1:7.2d)
Конфликты: погоня
Описание: Перейдите по символической ссылке и распечатайте ее целевой файл
Chase - это небольшая утилита для отслеживания фактического файла, на который указывает символическая ссылка, - если хотите, она преследует символическую ссылку. Результатом успешного запуска гарантированно будет существующий файл, который не является символической ссылкой.

Ты можешь использовать readlink с while петля. Функция ниже будет работать:

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

follow "/usr/bin/java"
Другие вопросы по тегам