Как я могу получить поведение readlink -f GNU на Mac? - PullRequest
330 голосов
/ 29 июня 2009

В Linux утилита readlink принимает опцию -f, которая идет по дополнительным ссылкам. Это не работает на Mac и, возможно, на системах BSD. Каким будет эквивалент?

Вот некоторая отладочная информация:

$ which readlink; readlink -f
/usr/bin/readlink
readlink: illegal option -f
usage: readlink [-n] [file ...]

Ответы [ 22 ]

6 голосов
/ 07 января 2017

Самый простой способ решить эту проблему и включить функциональность readlink на Mac с установленным Homebrew или FreeBSD или установить пакет 'coreutils'. Может также потребоваться в некоторых дистрибутивах Linux и других ОС POSIX.

Например, во FreeBSD 11 я установил, вызвав:

# pkg install coreutils

В MacOS с Homebrew команда будет выглядеть так:

$ brew install coreutils

Не совсем уверен, почему другие ответы такие сложные, вот и все, что нужно сделать. Файлы не находятся в другом месте, они просто еще не установлены.

3 голосов
/ 13 октября 2014

Ответов уже много, но у меня не получилось ни одного ... Так что сейчас я использую.

readlink_f() {
  local target="$1"
  [ -f "$target" ] || return 1 #no nofile

  while [ -L "$target" ]; do
    target="$(readlink "$target")" 
  done
  echo "$(cd "$(dirname "$target")"; pwd -P)/$target"
}
3 голосов
/ 08 октября 2013

Начать обновление

Это такая частая проблема, что мы собрали библиотеку Bash 4 для бесплатного использования (лицензия MIT) под названием realpath-lib . Он предназначен для эмуляции readlink -f по умолчанию и включает в себя два набора тестов для проверки (1), что он работает для данной системы Unix, и (2) против readlink -f , если установлен (но это не обязательно). Кроме того, его можно использовать для исследования, идентификации и разматывания глубоких, поврежденных символических ссылок и циклических ссылок, поэтому он может быть полезным инструментом для диагностики глубоко вложенных физических или символических проблем с каталогами и файлами. Его можно найти по адресу github.com или bitbucket.org .

Окончание обновления

Еще одно очень компактное и эффективное решение, которое не полагается ни на что, кроме Bash:

function get_realpath() {

    [[ ! -f "$1" ]] && return 1 # failure : file does not exist.
    [[ -n "$no_symlinks" ]] && local pwdp='pwd -P' || local pwdp='pwd' # do symlinks.
    echo "$( cd "$( echo "${1%/*}" )" 2>/dev/null; $pwdp )"/"${1##*/}" # echo result.
    return 0 # success

}

Сюда также входит параметр среды no_symlinks, который позволяет разрешать символические ссылки на физическую систему. Если для no_symlinks установлено что-то, то есть no_symlinks='on', символические ссылки будут разрешены для физической системы. В противном случае они будут применены (настройка по умолчанию).

Это должно работать в любой системе, которая предоставляет Bash, и возвращает код завершения, совместимый с Bash, для целей тестирования.

1 голос
/ 10 декабря 2015

По-настоящему не зависит от платформы и этот R-onliner

readlink(){ RScript -e "cat(normalizePath(commandArgs(T)[1]))" "$1";}

Чтобы имитировать readlink -f <path>, нужно будет использовать $ 2 вместо $ 1.

1 голос
/ 03 октября 2013

Лучше поздно, чем никогда, я полагаю. Я был мотивирован, чтобы разработать это специально, потому что мои скрипты Fedora не работали на Mac. Проблема в зависимостях и Bash. У Mac их нет, или, если они есть, они часто находятся где-то еще (другой путь). Манипулирование путями зависимости в кроссплатформенном скрипте Bash в лучшем случае является головной болью, а в худшем - угрозой безопасности, поэтому лучше по возможности избегать их использования.

Функция get_realpath () ниже проста, ориентирована на Bash, и никаких зависимостей не требуется. Я использую только встроенные команды Bash echo и cd . Это также довольно безопасно, так как все проверяется на каждом этапе пути и возвращает ошибки.

Если вы не хотите переходить по символическим ссылкам, поместите set -P в начало сценария, но в противном случае cd должен разрешить символические ссылки по умолчанию. Это было проверено с аргументами файла, которые являются {absolute | родственник | символическая ссылка | local} и возвращает абсолютный путь к файлу. До сих пор у нас не было проблем с этим.

function get_realpath() {

if [[ -f "$1" ]]
then
    # file *must* exist
    if cd "$(echo "${1%/*}")" &>/dev/null
    then
        # file *may* not be local
        # exception is ./file.ext
        # try 'cd .; cd -;' *works!*
        local tmppwd="$PWD"
        cd - &>/dev/null
    else
        # file *must* be local
        local tmppwd="$PWD"
    fi
else
    # file *cannot* exist
    return 1 # failure
fi

# reassemble realpath
echo "$tmppwd"/"${1##*/}"
return 0 # success

}

Вы можете комбинировать это с другими функциями get_dirname, get_filename, get_stemname и validate_path. Их можно найти в нашем репозитории GitHub как realpath-lib (полное раскрытие информации - это наш продукт, но мы предлагаем его бесплатно для сообщества без каких-либо ограничений). Он также может служить учебным пособием - он хорошо документирован.

Мы старались изо всех сил применять так называемые практики «современного Bash», но Bash - это важная тема, и я уверен, что всегда найдется место для улучшений. Требуется Bash 4+, но его можно настроить для работы со старыми версиями, если они все еще существуют.

1 голос
/ 21 января 2019

Поскольку моей работой пользуются люди с не-BSD Linux, а также с macOS, я выбрал использование этих псевдонимов в наших скриптах сборки (sed включено, поскольку у него есть похожие проблемы):

##
# If you're running macOS, use homebrew to install greadlink/gsed first:
#   brew install coreutils
#
# Example use:
#   # Gets the directory of the currently running script
#   dotfilesDir=$(dirname "$(globalReadlink -fm "$0")")
#   alias al='pico ${dotfilesDir}/aliases.local'
##

function globalReadlink () {
  # Use greadlink if on macOS; otherwise use normal readlink
  if [[ $OSTYPE == darwin* ]]; then
    greadlink "$@"
  else
    readlink "$@"
  fi
}

function globalSed () {
  # Use gsed if on macOS; otherwise use normal sed
  if [[ $OSTYPE == darwin* ]]; then
    gsed "$@"
  else
    sed "$@"
  fi
}

Необязательная проверка, которую вы можете добавить для автоматической установки homebrew + coreutils зависимости:

if [[ "$OSTYPE" == "darwin"* ]]; then
  # Install brew if needed
  if [ -z "$(which brew)" ]; then 
    /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"; 
  fi
  # Check for coreutils
  if [ -z "$(brew ls coreutils)" ]; then
    brew install coreutils
  fi
fi

Я полагаю, что это действительно "глобально", необходимо проверить других... но это, вероятно, близко к отметке 80/20.

0 голосов
/ 23 декабря 2015

Вот что я использую:

stat -f %N $your_path

0 голосов
/ 08 ноября 2014

Я написал утилиту реального пути для OS X , которая может дать те же результаты, что и readlink -f.


Вот пример:

(jalcazar@mac tmp)$ ls -l a
lrwxrwxrwx 1 jalcazar jalcazar 11  8月 25 19:29 a -> /etc/passwd

(jalcazar@mac tmp)$ realpath a
/etc/passwd


Если вы используете MacPorts, вы можете установить его с помощью следующей команды: sudo port selfupdate && sudo port install realpath.

0 голосов
/ 24 сентября 2014

Ответ от @Keith Smith дает бесконечный цикл.

Вот мой ответ, который я использую только в SunOS (SunOS пропускает так много команд POSIX и GNU).

Это файл сценария, который вы должны поместить в один из ваших каталогов $ PATH:

#!/bin/sh
! (($#)) && echo -e "ERROR: readlink <link to analyze>" 1>&2 && exit 99

link="$1"
while [ -L "$link" ]; do
  lastLink="$link"
  link=$(/bin/ls -ldq "$link")
  link="${link##* -> }"
  link=$(realpath "$link")
  [ "$link" == "$lastlink" ] && echo -e "ERROR: link loop detected on $link" 1>&2 && break
done

echo "$link"
0 голосов
/ 27 августа 2011

Perl имеет функцию чтения ссылок (например, Как мне скопировать символические ссылки в Perl? ). Это работает на большинстве платформ, включая OS X:

perl -e "print readlink '/path/to/link'"

Например:

$ mkdir -p a/b/c
$ ln -s a/b/c x
$ perl -e "print readlink 'x'"
a/b/c
...