Как я могу получить поведение GNU readlink-f на Mac?
в Linux readlink
утилита принимает параметр -f
это следует за дополнительными ссылками. Это, кажется, не работает на Mac и, возможно, на основе BSD систем. Каков будет эквивалент?
вот некоторая отладочная информация:
$ which readlink; readlink -f
/usr/bin/readlink
readlink: illegal option -f
usage: readlink [-n] [file ...]
20 ответов:
readlink -f
делает две вещи:
- он повторяет последовательность символических ссылок, пока не найдет фактический файл.
- он возвращает этот файл канон name-т. е. его абсолютный путь.
если вы хотите, вы можете просто построить сценарий оболочки, который использует поведение vanilla readlink для достижения того же самого. Вот вам пример. Очевидно, вы можете вставить это в свой собственный скрипт, где вы хотели бы позвонить
readlink -f
#!/bin/sh TARGET_FILE= 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
изменитьдо
если вы хотите иметь возможность используйте с
-f filename
как GNU readlink.
MacPorts и Homebrew обеспечивают coreutils пакет, содержащий
greadlink
(GNU readlink). Кредит Майклу Калвайту пост в mackb.com.brew install coreutils greadlink -f file.txt
вы можете быть заинтересованы в
realpath(3)
, или в Pythonos.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 этого скрипта должна быть короче. :)
Я ненавижу нагромождать еще одну реализацию, но мне нужно было a)портативная, чистая реализация оболочки; б) покрытие юнит-теста, так как количество крайних случаев для чего-то вроде этого нетривиальный.
посмотреть мой проект на Github для тестов и полный код. Ниже приводится краткий обзор реализации:
как проницательно указывает Кит Смит,
readlink -f
делает две вещи: 1) разрешает символические ссылки рекурсивно, и 2) канонизирует результат, таким образом:realpath() { canonicalize_path "$(resolve_symlinks "")" }
во-первых, реализация решателя символьных ссылок:
resolve_symlinks() { local dir_context path path=$(readlink -- "") if [ $? -eq 0 ]; then dir_context=$(dirname -- "") resolve_symlinks "$(_prepend_path_if_relative "$dir_context" "$path")" else printf '%s\n' "" fi } _prepend_path_if_relative() { case "" in /* ) printf '%s\n' "" ;; * ) printf '%s\n' "/" ;; esac }
обратите внимание, что это немного упрощенная версия полной реализации. полная реализация добавляет небольшую проверку для циклов символических ссылок, а также массажирует выход немного.
наконец, функция канонизации пути:
canonicalize_path() { if [ -d "" ]; then _canonicalize_dir_path "" else _canonicalize_file_path "" fi } _canonicalize_dir_path() { (cd "" 2>/dev/null && pwd -P) } _canonicalize_file_path() { local dir file dir=$(dirname -- "") file=$(basename -- "") (cd "$dir" 2>/dev/null && printf '%s/%s\n' "$(pwd -P)" "$file") }
вот и все, более или менее. Достаточно просто вставить в ваш скрипт, но достаточно сложно, чтобы Вы были сумасшедшими, чтобы полагаться на любой код, который не имеет модульных тестов для ваших случаев использования.
- установить homebrew
- запустите "brew install coreutils"
- запустить "greadlink-F path"
greadlink является гну и более ранних версий, который реализует -Ф. Вы можете использовать MacPorts или других, я предпочитаю самогон.
простой однострочный в perl, который обязательно будет работать почти везде без каких-либо внешних зависимостей:
perl -MCwd -e 'print Cwd::abs_path shift' ~/non-absolute/file
будет разыменовывать символические ссылки.
использование в скрипте может быть следующим:
readlinkf(){ perl -MCwd -e 'print Cwd::abs_path shift' "";} ABSPATH="$(readlinkf ./non-absolute/file)"
Я сделал скрипт под названием realpath лично, который выглядит немного так:
#!/usr/bin/env python import os.sys print os.path.realpath(sys.argv[1])
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
не хватает для форматов. В данном случае-stat -f%Y
может быть достаточно. Элемент-f%Y
опция покажет цель символической ссылки, тогда как-f%R
показывает абсолютный путь, соответствующий файл.EDIT:
если вы можете использовать Perl (Darwin / OS X поставляется с последними версиями
perl
) то:perl -MCwd=abs_path -le 'print abs_path readlink(shift);' linkedfile.txt
будет работать.
вот портативная функция оболочки, которая должна работать в любой Борн сопоставимые оболочки. Это позволит решить относительный путь пунктуации".. или. - и разыменование символических связей.
Если по какой-то причине у вас нет команды realpath(1) или readlink(1), это может быть псевдоним.
which realpath || alias realpath='real_path'
наслаждайтесь:
real_path () { OIFS=$IFS IFS='/' for I in 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 [ "" = "->" ] then FOO= 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-clause (freeBSD style). Копию лицензии можно найти, перейдя по указанной выше гиперссылке на мой сайт.
самый простой способ решить эту проблему и включить функциональность readlink на Mac с установленным Homebrew или FreeBSD-это установить пакет 'coreutils'. Также может потребоваться в некоторых дистрибутивах Linux и других ОС POSIX.
например, во FreeBSD 11 я установил, вызвав:
# pkg install coreutils
на MacOS с Homebrew, команда будет:
$ brew install coreutils
не совсем уверен, почему другие ответы так сложно, вот и все. Файлы не в другом месте, они просто еще не установлены.
Начать Обновление
Это такая частая проблема, что мы собрали библиотеку Bash 4 для бесплатного использования (лицензия MIT) под названием realpath-lib. Это предназначено для эмуляции readlink-f по умолчанию и включает в себя два набора тестов для проверки (1), что он работает для данной системы unix и (2) против readlink-f если установлен (но это не обязательно). Дополнительно, его можно использовать для того чтобы расследовать, определить и размотать глубокие, сломанные символические ссылки и циклические ссылки, поэтому он может быть полезным инструментом для диагностики глубоко вложенных физических или символических проблем с каталогами и файлами. Его можно найти по адресу github.com или bitbucket.org.
Обновления
еще одно очень компактное и эффективное решение, которое не полагается ни на что, кроме Bash:
function get_realpath() { [[ ! -f "" ]] && 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 код выхода для целей тестирования.
там уже много ответов, но ни один не работал для меня... Так вот что я использую сейчас.
readlink_f() { local target="" [ -f "$target" ] || return 1 #no nofile while [ -L "$target" ]; do target="$(readlink "$target")" done echo "$(cd "$(dirname "$target")"; pwd -P)/$target" }
лучше поздно, чем никогда, я полагаю. Я был мотивирован разработать это специально, потому что мои скрипты Fedora не работали на Mac. Проблема в зависимостях и Bash. У Macs их нет, или если они есть, они часто находятся где-то еще (другой путь). Манипуляции с путями зависимостей в кросс-платформенном сценарии Bash-это головная боль в лучшем случае и риск для безопасности в худшем - поэтому лучше избегать их использования, если это возможно.
функция get_realpath() ниже проста, Bash-ориентированный, и никаких зависимостей не требуется. Я использую только bash builtins Эхо и cd. Это также довольно безопасно, так как все проверяется на каждом этапе пути, и он возвращает условия ошибки.
Если вы не хотите следовать символическим ссылкам, то поставьте set-P в начале сценария, но в остальном cd должен разрешить символические ссылки по умолчанию. Он был протестирован с аргументами файла, которые являются {абсолютными / относительными | symlink / local} и возвращает абсолютный путь к файлу. До сих пор у нас не было никаких проблем с этим.
function get_realpath() { if [[ -f "" ]] 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+ , но может быть сделано для работы со старыми версиями, если они все еще вокруг.
истинно платформо-независимые будут также в этом Р-покатушки
readlink(){ RScript -e "cat(normalizePath(commandArgs(T)[1]))" "";}
На самом деле мимические
readlink -f <path>
, $2 вместо $1 нужно будет использовать.
я писал утилита 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
.
пути к readlink отличаются между моей системой и вашей. Пожалуйста, попробуйте указать полный путь:
/sw/sbin/readlink -f
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
ответ от @Keith Smith дает бесконечный цикл.
вот мой ответ, который я использую только на SunOS (SunOS пропускает так много команд POSIX и GNU).
это файл скрипта, который вы должны поместить в один из ваших каталогов $PATH:
#!/bin/sh ! (($#)) && echo -e "ERROR: readlink <link to analyze>" 1>&2 && exit 99 link="" 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"