Як я можу отримати поведінку зчитування GNU -f на 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`. Додано його до моєї відповіді нижче. Ура.
G. Cito

Відповіді:


181

readlink -f робить дві речі:

  1. Він повторюється по послідовності посилань, поки не знайде фактичний файл.
  2. Він повертає канонізоване ім'я файлу, тобто його абсолютне ім'я шляху.

Якщо ви хочете, ви можете просто створити скрипт оболонки, який використовує поведінку ванільного зчитування для досягнення того ж самого. Ось приклад. Очевидно, ви можете вставити це у свій власний сценарій, куди хочете зателефонувати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
Наскільки я можу сказати, це не спрацює, якщо батьківський dir шляху є симпосиланням. Напр. якщо 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

438

MacPorts і Homebrew надають пакет coreutils, що містить greadlink(readnlink GNU). Заслуга Майкла Калвейтта на сайті mackb.com.

brew install coreutils

greadlink -f file.txt

Якщо ви викликаєте readlink з інших місць, ніж bash, іншим рішенням буде видалити / usr / bin / readlink, а потім створити посилання на / usr / local / bin / greadlink.
chinglun

8
Не пишіть, якщо ви не a) пишете програмне забезпечення для свого власного b) не хочете возитися з іншими. Напишіть його таким чином, щоб він "просто працював" для когось.
kgadek

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/…
Pysis

43

Можливо, вас зацікавлять realpath(3)або Python's 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себе, чи не так?)
Телемах

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

По-перше, реалізація резолюції symlink:

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 - воно використовує не-POSIX localключове слово
go2null

Це не рівнозначно readlink -f, ані не realpath. Якщо припустити, що Mac має встановлену формулу coreutils (так із greadlinkдоступною), спробуйте це ln -s /tmp/linkeddir ~/Documents; greadlink -f /tmp/linkeddir/..друкує ваш домашній каталог (він вирішив /tmp/linkeddirпосилання перед тим, як застосувати ..батьківський dir ref), але ваш код створює /private/tmp/як /tmp/linkeddir/..не символьне посилання, але -d /tmp/linkeddir/..є істинним, і тому cd /tmp/linkeddir/..вас переносить /tmp, чий шлях є канонічним /private/tmp. Ваш код робить те, що realpath -Lробить, натомість.
Martijn Pieters

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. Запустити "варити встановити coreutils"
  3. Запустити "greadlink -f path"

greadlink - це gnu readlink, який реалізує -f. Ви також можете використовувати макпорти або інші, я віддаю перевагу домашньому пиву.


2
Я не бачу різниці у відповіді, наданій у 2010 році stackoverflow.com/a/4031502/695671
Jason S

Якщо вам потрібно використовувати ці команди зі своїми звичайними іменами, ви можете додати каталог "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. Оскільки ОП 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])'"
codeniffer

11

Як що до цього?

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

Це єдине рішення, яке працює для мене; Мені потрібно проаналізувати такі шляхи, як ../../../->/
keflavich

це не може працювати, якщо у вас є, /usr/bin/наприклад, симпосилання, як це alternativesлюбить мати система.
акостадінов

Не працює для окремих файлів, лише каталогів. GNU readlink працює з каталогами, файлами, посиланнями тощо
codeniffer

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

також, на всякий випадок, коли когось цікавить ось як реалізувати базове ім’я та ім’я 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

EDIT: Цей код ліцензований за умовами ліцензії 2-го класу (стиль freeBSD). Копію ліцензії можна знайти, перейшовши за вищенаведеним гіперпосиланням на мій сайт.


Real_path повертає / ім'я файлу замість / path / to / filename на Mac OS X Lion в оболонці bash.

@Barry Те ж саме відбувається з 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.
G. Cito

Команда Perl не працює правильно 10.15. Дайте йому файл у поточному каталозі, і він видає поточний каталог (без файлу).
codeniffer

7

Найпростіший спосіб вирішити цю проблему та включити функціональність readlink на Mac w / Homebrew, встановленому або FreeBSD, - це встановити пакет 'coreutils'. Може знадобитися і для певних дистрибутивів Linux та інших POSIX OS.

Наприклад, у FreeBSD 11 я встановив:

# pkg install coreutils

На MacOS з Homebrew командою було б:

$ brew install coreutils

Не дуже впевнений, чому інші відповіді такі складні, це все, що там є. Файли не в іншому місці, вони просто ще не встановлені.


7
brew install coreutils --with-default-namesщоб бути більш конкретним. Або замість цього ви поставите "greadlink" ...
Henk

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

3

Почніть оновлення

Це настільки часта проблема, що ми створили бібліотеку Bash 4 для вільного використання (Ліцензія MIT) під назвою realpath-lib . Він призначений для імітації readlink -f за замовчуванням і включає два тестових набору для перевірки (1), що він працює для даної системи Unix та (2) проти readlink -f, якщо встановлений (але це не потрібно). Крім того, він може бути використаний для дослідження, виявлення та розкручування глибоких, ламаних символьних посилань та кругових посилань, тому може бути корисним інструментом для діагностики глибоко вкладених фізичних чи символьних проблем із файлами та файлами. Його можна знайти на сайті github.com або bitbucket.org .

Закінчити оновлення

Ще одне дуже компактне та ефективне рішення, яке не покладається ні на що, окрім Баша, це:

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 - це головний біль у кращому випадку та ризик для безпеки в гіршому випадку, тому краще уникати їх використання, якщо це можливо.

Наведена нижче функція get_realpath () проста, орієнтована на Баш, і не потрібно ніяких залежностей. Я використовую лише Bash buildins echo та cd . Він також досить безпечний, оскільки все тестується на кожному етапі шляху і він повертає умови помилок.

Якщо ви не хочете слідувати за посиланнями, то поставте set -P в передній частині сценарію, але в іншому випадку cd повинен вирішити символьні посилання за замовчуванням. Це перевірено аргументами файлів, які є абсолютними відносний | symlink | 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 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
}

Необов’язковий чек, який можна додати, щоб автоматично встановити домашню мову залежності + 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 ("Core Utilities"), але вони дещо відрізняються (наприклад, мають різні прапори).

Це тому, що в 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-он-лайн

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

Щоб наслідувати readlink -f <path>, потрібно було б використати 2 долари замість 1 долара.


За винятком R майже не існує жодної системи за замовчуванням. Ще не на Mac (станом на 10.15), про що йдеться у цьому питанні.
codeniffer

0

readlink -fРеалізація сумісних з POSIX сценаріями оболонок POSIX

https://github.com/ko1nksm/readlinkf

Це сумісно з POSIX (без башизму). Він не використовує ні readlinkані realpath. Я перевірив, що це абсолютно те саме, порівнюючи з GNU readlink -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
Ага .. тому фінк містить заміну на readlinkте, що gnu сумісний. Це приємно знати, але це не вирішує мою проблему, оскільки мені потрібен мій сценарій для роботи на машині інших народів, і я не можу вимагати від них встановлення фінка для цього.
troelskn

Ви не відповідаєте на початкове запитання. Будь ласка, перепишіть свою відповідь.
Хайме Хаблуцель
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.