Как я могу получить поведение readlink -f GNU на Mac?


377

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

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

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

1
Немного поздно, но в вашем вопросе отсутствует упоминание об используемой вами оболочке. Это актуально, поскольку readlinkможет быть встроенной или внешней командой.
0xC0000022L

1
Это могло бы объяснить разницу, возможно. Я вполне уверен, что использовал bash в обоих случаях.
troelskn

1
Почему эта опция запрещена на Mac?
CommaToast

3
Мне бы очень хотелось, чтобы Apple сделала так, чтобы OS X поддерживала больше путей Linux по умолчанию, и обращалась к таким вещам. Они могли бы сделать это, ничего не нарушая, не так ли?
CommaToast

1
@CommaToast Ну, они поставляются с Perl, так что ++ :-) ... откройте Terminal.app и введите: touch myfile ; ln -s myfile otherfile ; perl -MCwd=abs_path -le 'print abs_path readlink(shift);' otherfile... в моем случае я вижу: / Users / cito / myfile`. Добавил его в мой ответ ниже. Приветствия.
Г. Сито

Ответы:


181

readlink -f делает две вещи:

  1. Он перебирает последовательность символических ссылок, пока не находит фактический файл.
  2. Он возвращает канонизированное имя этого файла, т. Е. Его абсолютный путь.

Если вы хотите, вы можете просто создать сценарий оболочки, который использует ванильное поведение readlink для достижения того же самого. Вот пример. Очевидно, вы могли бы вставить это в свой собственный скрипт, где вы хотели бы позвонитьreadlink -f

#!/bin/sh

TARGET_FILE=$1

cd `dirname $TARGET_FILE`
TARGET_FILE=`basename $TARGET_FILE`

# Iterate down a (possible) chain of symlinks
while [ -L "$TARGET_FILE" ]
do
    TARGET_FILE=`readlink $TARGET_FILE`
    cd `dirname $TARGET_FILE`
    TARGET_FILE=`basename $TARGET_FILE`
done

# Compute the canonicalized name by finding the physical path 
# for the directory we're in and appending the target file.
PHYS_DIR=`pwd -P`
RESULT=$PHYS_DIR/$TARGET_FILE
echo $RESULT

Обратите внимание, что это не включает обработку ошибок. Особенно важно, что он не обнаруживает циклы символических ссылок. Простой способ сделать это - посчитать, сколько раз вы обойдете цикл и потерпите неудачу, если наберете невероятно большое число, например 1000.

ИЗМЕНЕНО для использования pwd -Pвместо $PWD.

Обратите внимание, что этот сценарий будет вызываться как ./script_name filename, нет -f, изменится $1на, $2если вы хотите иметь возможность использовать -f filenameподобную GNU readlink.


1
Насколько я могу судить, это не сработает, если родительский каталог пути является символической ссылкой. Например. если foo -> /var/cux, то foo/barне будет решен, потому что barэто не ссылка, хотя fooесть.
troelskn

1
Ах. Да. Это не так просто, но вы можете обновить приведенный выше скрипт, чтобы справиться с этим. Я отредактирую (переписать, действительно) ответ соответственно.
Кит Смит

Ну, ссылка может быть где угодно в пути. Я предполагаю, что сценарий может повторяться по каждой части пути, но тогда он становится немного сложным.
troelskn

1
Спасибо. Проблема в том, что $ PWD предоставляет нам логический рабочий каталог, основанный на значениях символических ссылок, по которым мы следовали. Мы можем получить настоящий физический каталог с помощью 'pwd -P'. Он должен вычислить его, выполнив ".." до корня файловой системы. Я обновлю сценарий в своем ответе соответственно.
Кит Смит

12
Отличное решение, но оно не работает должным образом с путями, которые необходимо экранировать , такими как имена файлов или каталогов со встроенными пробелами . Чтобы исправить это, используйте cd "$(dirname "$TARGET_FILE")" и TARGET_FILE=$(readlink "$TARGET_FILE")и TARGET_FILE=$(basename "$TARGET_FILE")в соответствующих местах в приведенном выше коде.
mklement0

439

MacPorts и Homebrew предоставляют пакет coreutils , содержащий greadlink(GNU readlink). Благодарим Майкла Каллвейта от mackb.com.

brew install coreutils

greadlink -f file.txt

Если вы вызываете readlink из других мест, кроме bash, другим решением будет удалить / usr / bin / readlink, а затем создать ссылку на / usr / local / bin / greadlink.
Chinglun

8
Пожалуйста, не делайте, если вы а) не пишете программное обеспечение для себя, б) не хотите связываться с другими. Напишите его так, чтобы он «просто работал» для всех.
кгадек

14
@ching Сделайте это вместо этого в своем .bashrc:export PATH="/usr/local/opt/coreutils/libexec/gnubin:$PATH"
bfontaine

1
Вопрос просит эквивалент в системе OS X или BSD, это не отвечает на это. Вам также нужно будет установить Homebrew. Вы также будете устанавливать множество вещей, кроме GNU Readlink. Если вы пишете скрипт для стандартных установок OS X, этот ответ не поможет. Вы можете использовать комбинацию pwd -Pи readlinkна OS X, не устанавливая ничего
Jason S

4
Я хотел, чтобы GNU и другие утилиты на моем Mac работали хорошо, без префикса, и были легко доступны для моего пользователя PATH. Сделал выше, начиная с brew install <package> --default-names. Много раз на вопрос на этом сайте отвечали немного, или более того, за пределами технических требований автора, но в аналогичной ситуации, и все еще был очень полезен. topbug.net/blog/2013/04/14/…
Пизис

43

Вас могут заинтересовать realpath(3)или Python os.path.realpath. Два не совсем то же самое; вызов библиотеки C требует, чтобы компоненты промежуточного пути существовали, а версия Python - нет.

$ pwd
/tmp/foo
$ ls -l
total 16
-rw-r--r--  1 miles    wheel  0 Jul 11 21:08 a
lrwxr-xr-x  1 miles    wheel  1 Jul 11 20:49 b -> a
lrwxr-xr-x  1 miles    wheel  1 Jul 11 20:49 c -> b
$ python -c 'import os,sys;print(os.path.realpath(sys.argv[1]))' c
/private/tmp/foo/a

Я знаю, что вы сказали, что предпочтете что-то более легкое, чем другой язык сценариев, но на случай, если компиляция двоичного файла является невыносимой, вы можете использовать Python и ctypes (доступные в Mac OS X 10.5) для переноса вызова библиотеки:

#!/usr/bin/python

import ctypes, sys

libc = ctypes.CDLL('libc.dylib')
libc.realpath.restype = ctypes.c_char_p
libc.__error.restype = ctypes.POINTER(ctypes.c_int)
libc.strerror.restype = ctypes.c_char_p

def realpath(path):
    buffer = ctypes.create_string_buffer(1024) # PATH_MAX
    if libc.realpath(path, buffer):
        return buffer.value
    else:
        errno = libc.__error().contents.value
        raise OSError(errno, "%s: %s" % (libc.strerror(errno), buffer.value))

if __name__ == '__main__':
    print realpath(sys.argv[1])

По иронии судьбы версия C этого скрипта должна быть короче. :)


Да, realpathэто действительно то, чего я хочу. Но кажется довольно неловким, что мне нужно скомпилировать двоичный файл, чтобы получить эту функцию из сценария оболочки.
troelskn

4
Почему бы тогда не использовать однострочник Python в сценарии оболочки? (Не так ли отличается от однострочного звонка самому readlinkсебе, не так ли?)
Telemachus

3
python -c "import os,sys; print(os.path.realpath(os.path.expanduser(sys.argv[1])))" "${1}"работает с путями, как'~/.symlink'
Уэс Тернер

@ troelskin Я не знал, что Perl, Python и т. д. были "разрешены" !! ... в этом случае я собираюсь добавить perl -MCwd=abs_path -le 'print abs_path readlink(shift);'к своему ответу :-)
G. Cito

27

Ненавижу накапливать еще одну реализацию, но мне понадобились: а) портативная реализация с чистой оболочкой и б) охват модульным тестом , так как количество граничных случаев для чего-то подобного нетривиально .

Смотрите мой проект на Github для тестов и полного кода. Далее следует краткий обзор реализации:

Как со всей проницательностью указывает Кит Смит, он readlink -fделает две вещи: 1) рекурсивно разрешает символические ссылки и 2) канонизирует результат, следовательно:

realpath() {
    canonicalize_path "$(resolve_symlinks "$1")"
}

Во-первых, реализация распознавателя символической ссылки:

resolve_symlinks() {
    local dir_context path
    path=$(readlink -- "$1")
    if [ $? -eq 0 ]; then
        dir_context=$(dirname -- "$1")
        resolve_symlinks "$(_prepend_path_if_relative "$dir_context" "$path")"
    else
        printf '%s\n' "$1"
    fi
}

_prepend_path_if_relative() {
    case "$2" in
        /* ) printf '%s\n' "$2" ;;
         * ) printf '%s\n' "$1/$2" ;;
    esac 
}

Обратите внимание, что это слегка упрощенная версия полной реализации . Полная реализация добавляет небольшую проверку циклов символической ссылки , а также немного массирует вывод.

Наконец, функция для канонизации пути:

canonicalize_path() {
    if [ -d "$1" ]; then
        _canonicalize_dir_path "$1"
    else
        _canonicalize_file_path "$1"
    fi
}   

_canonicalize_dir_path() {
    (cd "$1" 2>/dev/null && pwd -P) 
}           

_canonicalize_file_path() {
    local dir file
    dir=$(dirname -- "$1")
    file=$(basename -- "$1")
    (cd "$dir" 2>/dev/null && printf '%s/%s\n' "$(pwd -P)" "$file")
}

Вот и все, более или менее. Достаточно простой для вставки в ваш скрипт, но достаточно хитрый, чтобы вы с ума сошли, полагаясь на любой код, не имеющий модульных тестов для ваших сценариев использования.


3
Это не POSIX - используется localключевое слово не POSIX
go2null

Это не эквивалентно readlink -fни realpath. Предполагая, что Mac с установленной формулой coreutils (то есть с greadlinkдоступной), попробуйте это с ln -s /tmp/linkeddir ~/Documents; greadlink -f /tmp/linkeddir/..печатает ваш домашний каталог (он разрешил /tmp/linkeddirссылку перед применением ..родительской ссылки dir), но ваш код выдает /private/tmp/as /tmp/linkeddir/..не символическую ссылку, а значение -d /tmp/linkeddir/..true, и поэтому cd /tmp/linkeddir/..вы переходите к тому /tmp, чей канонический путь есть /private/tmp. Ваш код делает то realpath -L, что делает, вместо этого.
Мартин Питерс

27

Простой однострочный в Perl, который будет работать практически везде без каких-либо внешних зависимостей:

perl -MCwd -e 'print Cwd::abs_path shift' ~/non-absolute/file

Будет разыменовывать символические ссылки.

Использование в скрипте может быть таким:

readlinkf(){ perl -MCwd -e 'print Cwd::abs_path shift' "$1";}
ABSPATH="$(readlinkf ./non-absolute/file)"

4
чтобы получить завершающий -lperl -MCwd -le 'print Cwd::abs_path shift' ~/non-absolute/file
символ

26
  1. Установить доморощенный
  2. Запустите "brew install coreutils"
  3. Запустите "greadlink -f путь"

greadlink - это ссылка для чтения gnu, которая реализует -f. Вы можете использовать MacPort или другие, я предпочитаю доморощенный.


2
Я не вижу никакой разницы с ответом, данным в 2010 году stackoverflow.com/a/4031502/695671
Джейсон С

Если вам нужно использовать эти команды с их обычными именами, вы можете добавить каталог «gnubin» в ваш PATH из вашего bashrc, например: PATH = «/ usr / local / opt / coreutils / libexec / gnubin: $ PATH»
Денис Трофимов

20

Я сделал скрипт под названием realpath лично, который выглядит примерно так:

#!/usr/bin/env python
import os.sys
print os.path.realpath(sys.argv[1])

Вы должны изменить это на, sys.argv[1]если вы хотите, чтобы скрипт печатал реальный путь первого аргумента. sys.argv[0]просто печатает реальный путь самого скрипта pyton, что не очень полезно.
Якоб Эггер

17
Вот псевдоним для ~/.profile:alias realpath="python -c 'import os, sys; print os.path.realpath(sys.argv[1])'"
jwhitlock

Отличный ответ и отличный однострочный @jwhitlock. С тех пор как цитируется OP readlink -f, вот модифицированная версия псевдонима @jwhitlock для поддержки возможного -fфлага: alias realpath="python -c 'import os, sys; print os.path.realpath(sys.argv[2] if sys.argv[1] == \"-f\" else sys.argv[1])'"
codesniffer

11

Что насчет этого?

function readlink() {
  DIR="${1%/*}"
  (cd "$DIR" && echo "$(pwd -P)")
}

Это единственное решение, которое работает для меня; Мне нужно разобрать пути как ../../../->/
keflavich

это не может работать, если у вас есть символическая ссылка, /usr/bin/например, как alternativesсистема любит иметь.
Акостадинов

Не работает для отдельных файлов, только для каталогов. GNU readlink работает с каталогами, файлами, символическими
ссылками

7

Вот портативная функция оболочки, которая должна работать в любой похожей оболочке Bourne. Это разрешит пунктуацию относительного пути ".. или." и разыменование символических ссылок.

Если по какой-либо причине у вас нет команды realpath (1) или readlink (1), это может быть псевдоним.

which realpath || alias realpath='real_path'

Наслаждаться:

real_path () {
  OIFS=$IFS
  IFS='/'
  for I in $1
  do
    # Resolve relative path punctuation.
    if [ "$I" = "." ] || [ -z "$I" ]
      then continue
    elif [ "$I" = ".." ]
      then FOO="${FOO%%/${FOO##*/}}"
           continue
      else FOO="${FOO}/${I}"
    fi

    ## Resolve symbolic links
    if [ -h "$FOO" ]
    then
    IFS=$OIFS
    set `ls -l "$FOO"`
    while shift ;
    do
      if [ "$1" = "->" ]
        then FOO=$2
             shift $#
             break
      fi
    done
    IFS='/'
    fi
  done
  IFS=$OIFS
  echo "$FOO"
}

Кроме того, на случай, если кто-то заинтересован, вот как реализовать basename и dirname в 100% чистом шелл-коде:

## http://www.opengroup.org/onlinepubs/000095399/functions/dirname.html
# the dir name excludes the least portion behind the last slash.
dir_name () {
  echo "${1%/*}"
}

## http://www.opengroup.org/onlinepubs/000095399/functions/basename.html
# the base name excludes the greatest portion in front of the last slash.
base_name () {
  echo "${1##*/}"
}

Вы можете найти обновленную версию этого шелл-кода на моем сайте Google: http://sites.google.com/site/jdisnard/realpath

РЕДАКТИРОВАТЬ: Этот код лицензируется в соответствии с условиями 2-пунктовой (стиль FreeBSD) лицензии. Копию лицензии можно найти, перейдя по указанной выше гиперссылке на мой сайт.


Real_path возвращает / filename вместо / path / to / filename в Mac OS X Lion в оболочке bash.

@ Барри То же самое происходит на OSX Snow Leopard. Хуже того, последовательные вызовы real_path () объединяют вывод!
Доминик

@Dominique: не удивительно, так как внутренняя часть функции не работает в своей собственной оболочке ... действительно уродливой и хрупкой.
0xC0000022L

7

FreeBSD и OSX имеют версию, statпроизводную от NetBSD.

Вы можете настроить вывод с помощью переключателей формата (см. Страницы руководства по ссылкам выше).

%  cd  /service
%  ls -tal 
drwxr-xr-x 22 root wheel 27 Aug 25 10:41 ..
drwx------  3 root wheel  8 Jun 30 13:59 .s6-svscan
drwxr-xr-x  3 root wheel  5 Jun 30 13:34 .
lrwxr-xr-x  1 root wheel 30 Dec 13 2013 clockspeed-adjust -> /var/service/clockspeed-adjust
lrwxr-xr-x  1 root wheel 29 Dec 13 2013 clockspeed-speed -> /var/service/clockspeed-speed
% stat -f%R  clockspeed-adjust
/var/service/clockspeed-adjust
% stat -f%Y  clockspeed-adjust
/var/service/clockspeed-adjust

Некоторые версии OS X stat могут не иметь -f%Rопции для форматов. В этом случае -stat -f%Yможет быть достаточно. -f%YВариант будет показывать цель символьной ссылки, в то время как -f%Rпоказывает абсолютный путь , соответствующий файл.

РЕДАКТИРОВАТЬ:

Если вы можете использовать Perl (Darwin / OS X поставляется с последними версиями perl), то:

perl -MCwd=abs_path -le 'print abs_path readlink(shift);' linkedfile.txt

будет работать.


2
Это верно для FreeBSD 10, но не для OS X 10.9.
Занчи

Хороший улов. Упомянутая страница руководства по статистике предназначена для OS / X 10.9. RВозможность -f%отсутствует. Возможно, stat -f%Yдает желаемый результат. Я буду корректировать ответ. Обратите внимание, что statинструмент появился во FreeBSD в версии 4.10 и NetBSD в версии 1.6.
Г. Сито

Команда Perl не работает правильно на 10.15. Дайте ему файл в текущем каталоге, и он даст текущий каталог (без файла).
кодснифер

7

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

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

# pkg install coreutils

На MacOS с Homebrew команда будет такой:

$ brew install coreutils

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


7
brew install coreutils --with-default-namesбыть более конкретным. Или вместо этого вы получите «greadlink» ...
Хенк

Я не вижу никакой разницы в ответах от 2010 stackoverflow.com/a/4031502/695671 и 2012 stackoverflow.com/a/9918368/695671 Вопрос на самом деле "... на Mac и, возможно, на системах BSD. Что бы эквивалент будет? Я не верю, что это очень хороший ответ на этот вопрос. Причин много, но вы можете ориентироваться на компьютеры Mac OS без возможности что-либо установить. pwd -Pдовольно просто, но он исчезает среди многих других ответов, в том числе повторяющихся, отсюда и отрицательный ответ.
Джейсон С

3

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

Это настолько частая проблема, что мы собрали библиотеку 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-совместимый код завершения для целей тестирования.


3

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

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"
}

1
Это не работает, если $targetпуть, это работает, только если это файл.
MaxGhost

3

Ленивый способ, который работает для меня,

$ brew install coreutils
$ ln -s /usr/local/bin/greadlink /usr/local/bin/readlink
$ which readlink
/usr/local/bin/readlink
/usr/bin/readlink

1

Наверное, лучше поздно, чем никогда. Я был мотивирован, чтобы разработать это специально, потому что мои скрипты 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

Так как моя работа используется людьми с не-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.


1

объяснение

Coreutils является brew пакет, который устанавливает основные утилиты GNU / Linux, соответствующие их реализации в Mac OSX, чтобы вы могли использовать их

Вы можете найти программы или утилиты в вашей системе Mac OSX, которые кажутся похожими на Linux coreutils («Утилиты ядра»), но в некоторых случаях они различаются (например, имеют разные флаги).

Это потому, что реализация этих инструментов в Mac OSX отличается. Чтобы получить исходное поведение, подобное GNU / Linux, вы можете установить coreutilsпакет через систему brewуправления пакетами.

Это установит соответствующие основные утилиты с префиксом g. Например readlink, вы найдете соответствующую greadlinkпрограмму.

Для того, чтобы заставить readlinkработать как реализация GNU readlink( greadlink), вы можете создать простой псевдоним после установки coreutils.

Реализация

  1. Установить варку

Следуйте инструкциям на https://brew.sh/

  1. Установите пакет coreutils

brew install coreutils

  1. Создать псевдоним

Вы можете поместить свой псевдоним в ~ / .bashrc, ~ / .bash_profile или в любое место, где вы храните псевдонимы bash. Я лично держу свои в ~ / .bashrc

alias readlink=greadlink

Вы можете создать аналогичные псевдонимы для других coreutils, таких как gmv, gdu, gdf и так далее. Но имейте в виду, что поведение GNU на компьютере Mac может сбивать с толку других, привыкших работать с нативным coreutils, или может вести себя неожиданно в вашей системе Mac.


0

Я написал утилиту realpath для 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

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

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

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


За исключением R вряд ли в каких-либо системах по умолчанию. Пока не на Mac (по состоянию на 10.15), о чем этот вопрос.
кодснифер

0

POSIX-совместимая readlink -fреализация для сценариев оболочки POSIX

https://github.com/ko1nksm/readlinkf

Это POSIX-совместимый (без bashism). Это не использует ни, readlinkни realpath. Я проверил, что это точно так же, сравнивая с GNUreadlink -f (см. Результаты теста ). Имеет обработку ошибок и хорошую производительность. Вы можете смело заменить с readlink -f. Лицензия является CC0, поэтому вы можете использовать ее для любого проекта.

# POSIX compliant version
readlinkf_posix() {
  [ "${1:-}" ] || return 1
  target=$1 max_symlinks=10 CDPATH=''

  [ -e "${target%/}" ] || target=${1%"${1##*[!/]}"} # trim trailing slashes
  [ -d "${target:-/}" ] && target="$target/"

  cd -P . 2>/dev/null || return 1
  while [ "$max_symlinks" -gt 0 ] && max_symlinks=$((max_symlinks - 1)); do
    if [ ! "$target" = "${target%/*}" ]; then
      cd -P "${target%/*}/" 2>/dev/null || break
      target=${target##*/}
    fi

    if [ ! -L "$target" ]; then
      target="${PWD%/}${target:+/}$target"
      printf '%s\n' "${target:-/}"
      return 0
    fi

    # `ls -dl` format: "%s %u %s %s %u %s %s -> %s\n",
    #   <file mode>, <number of links>, <owner name>, <group name>,
    #   <size>, <date and time>, <pathname of link>, <contents of link>
    # https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html
    link=$(ls -dl "$target" 2>/dev/null) || break
    target=${link#*" $target -> "}
  done
  return 1
}

Пожалуйста, обратитесь к последнему коду. Это может быть исправлено.


-2

В Perl есть функция readlink (например, Как мне скопировать символические ссылки в 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

5
Это делает то же самое, что и readlinkбез -fопции - без рекурсии, без абсолютного пути - так что вы можете также использовать readlinkнапрямую.
mklement0

-2

Ответ от @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"


-5

Пути к readlink различны между моей системой и вашей. Пожалуйста, попробуйте указать полный путь:

/sw/sbin/readlink -f


1
И в чем конкретно разница между / sw / sbin и / usr / bin? И почему два бинарных файла отличаются?
troelskn

4
Ага .. так что fink содержит замену, readlinkкоторая совместима с GNU. Приятно знать, но это не решает мою проблему, так как мне нужно, чтобы мой скрипт запускался на машине других людей, и я не могу требовать от них установки fink для этого.
troelskn

Вы не отвечаете на оригинальный вопрос. Пожалуйста, перепишите свой ответ.
Хайме
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.