كيف يمكنني الحصول على سلوك readlink -f الخاص بـ GNU على جهاز Mac؟

StackOverflow https://stackoverflow.com/questions/1055671

  •  20-08-2019
  •  | 
  •  

سؤال

على لينكس، readlink الأداة المساعدة تقبل خيارا -f الذي يتبع روابط إضافية.يبدو أن هذا لا يعمل على أنظمة Mac وربما الأنظمة المستندة إلى BSD.ماذا سيكون المعادل؟

إليك بعض معلومات التصحيح:

$ which readlink; readlink -f
/usr/bin/readlink
readlink: illegal option -f
usage: readlink [-n] [file ...]
هل كانت مفيدة؟

المحلول

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 مثل رابط قراءة جنو.

نصائح أخرى

وMacPorts والبيرة توفر <م> coreutils حزمة تحتوي على greadlink (GNU readlink). الائتمان إلى آخر مايكل Kallweitt في mackb.com.

brew install coreutils

greadlink -f file.txt

وقد تكون مهتمة في realpath(3) ، أو في بيثون os.path.realpath . وهما ليس بالضبط نفس. يتطلب دعوة مكتبة C التي مكونات مسار الوسيطة موجودة، في حين أن النسخة بيثون لا.

$ 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

وأنا أعلم قلت لك تفضل شيئا أكثر خفيفة الوزن من لغة البرمجة آخر، ولكن فقط في حالة تجميع ثنائي ولا يطاق، يمكنك استخدام بيثون و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 من هذا السيناريو. :)

أنا أكره أن أتراكم مع تنفيذ آخر، ولكن كنت بحاجة إلى) تطبيق شل محمول ونقي, ، وب) تغطية اختبار الوحدة, ، مثل عدد حالات الحافة لشيء مثل هذا غير تافهة.

يرى مشروعي على جيثب للاختبارات والتعليمات البرمجية الكاملة.وفيما يلي ملخص للتنفيذ:

وكما يشير كيث سميث بذكاء، 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")
}

هذا كل شيء، أكثر أو أقل.بسيطة بما يكفي للصقها في البرنامج النصي الخاص بك، ولكنها صعبة بما يكفي لدرجة أنك ستصاب بالجنون عند الاعتماد على أي كود لا يحتوي على اختبارات وحدة لحالات الاستخدام الخاصة بك.

  1. تثبيت البيرة المنزلية
  2. قم بتشغيل "brew install coreutils"
  3. تشغيل "مسار greadlink -f"

greadlink هو رابط قراءة gnu الذي ينفذ -f.يمكنك استخدام macports أو غيرها أيضًا، وأنا أفضل البيرة المنزلية.

وبسيطة أونيلينير في بيرل للتأكد من أن يعمل في كل مكان تقريبا من دون أي تبعيات الخارجية:

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

وويل روابط الرمزية dereference.

والاستخدام في برنامج نصي يمكن أن يكون مثل هذا:

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

ولقد تقدمت النصي دعا realpath شخصيا والتي تبدو شيئا قليلا مثل:

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

وماذا عن هذا؟

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

فري بي إس دي و أو إس إكس لديك نسخة من 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

سيعمل.

وهنا هي وظيفة قذيفة المحمولة التي يجب أن تعمل في ANY بورن قذيفة مماثلة. وسوف يحل علامات الترقيم مسار نسبي ".. أو". وdereference صلات رمزية.

إذا لسبب ما لم يكن لديك 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##*/}"
}

ويمكنك العثور على نسخة من هذا الرمز shell المحدثة في موقع جوجل بلدي: مواقع http: //. google.com/site/jdisnard/realpath

وتحرير: تم ترخيص هذا كود بموجب شروط الترخيص 2-الفقرة (نمط فري). ويمكن الاطلاع على نسخة من الرخصة باتباع الارتباط التشعبي أعلاه لموقعي.

وأسهل طريقة لحل هذه المشكلة وتمكين وظيفة readlink على ماك ث / تثبيت البيرة أو فري هو لتثبيت حزمة 'coreutils. قد يكون من الضروري أيضا على بعض توزيعات لينكس وغيرها من POSIX OS.

وعلى سبيل المثال، في فري 11، أنا ركبت من خلال التذرع:

و# pkg install coreutils

في ماك مع البيرة، فإن الأمر على النحو التالي:

و$ brew install coreutils

ولست متأكدا حقا لماذا الإجابات الأخرى هي معقدة جدا، وهذا كل ما في الامر. وليس في مكان آخر، وانهم فقط لم يتم تثبيت الملفات حتى الان.

ابدأ التحديث

هذه مشكلة متكررة لدرجة أننا قمنا بتجميع مكتبة Bash 4 للاستخدام المجاني (ترخيص MIT) تسمى realpath-lib.تم تصميم هذا لمحاكاة رابط القراءة -f افتراضيًا ويتضمن مجموعتي اختبار للتحقق (1) من أنه يعمل مع نظام يونكس معين و(2) ضده رابط القراءة -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 لأغراض الاختبار.

وهناك بالفعل الكثير من الإجابات، ولكن أيا منها لم يعمل بالنسبة لي ... وهذا هو ما أنا باستخدام الآن.

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

أن تأتي متأخرا أفضل من ألا تأتي أبدا، على ما أعتقد.لقد كنت متحمسًا لتطوير هذا على وجه التحديد لأن نصوص Fedora الخاصة بي لم تكن تعمل على نظام التشغيل Mac.المشكلة هي التبعيات وباش.لا تتوفر هذه العناصر في أجهزة Mac، أو إذا كانت موجودة، فغالبًا ما تكون في مكان آخر (مسار آخر).يعد التلاعب بمسار التبعية في برنامج Bash النصي عبر الأنظمة الأساسية بمثابة صداع في أحسن الأحوال ومخاطر أمنية في أسوأ الأحوال - لذا فمن الأفضل تجنب استخدامها، إن أمكن.

الدالة get_realpath() أدناه بسيطة، وتتمحور حول Bash، ولا توجد حاجة إلى أي تبعيات.أنا أستخدم فقط Bash المدمج صدى صوت و قرص مضغوط.كما أنه آمن إلى حد ما، حيث يتم اختبار كل شيء في كل مرحلة من الطريق ويعرض حالات الخطأ.

إذا كنت لا ترغب في اتباع الروابط الرمزية، فقم بوضع مجموعة -P في مقدمة البرنامج النصي، ولكن خلاف ذلك قرص مضغوط يجب أن يحل الارتباطات الرمزية بشكل افتراضي.تم اختباره مع وسيطات الملفات التي هي {Absolute | قريب | symlink | محلي} ويعيد المسار المطلق إلى الملف.حتى الآن لم نواجه أي مشاكل معها.

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+ ولكن يمكن جعله يعمل مع الإصدارات الأقدم إذا كانت لا تزال موجودة.

وحقا منصة indpendent سيكون أيضا هذا R-onliner

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

لreadlink -f <path> تقليد الواقع، $ 2 بدلا من 1 $ في حاجة لاستخدامها.

ومنذ يستخدم عملي من قبل الناس مع غير BSD لينكس وكذلك ماك، لقد اختارت لاستخدام هذه الأسماء المستعارة في البرامج النصية بناء لدينا (المدرجة 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
}

والاختياري تحقق يمكنك إضافة إلى تثبيت البيرة + <لأ href = "تلقائيا: //formulae.brew .sh / صيغة / coreutils "يختلط =" نوفولو noreferrer "> 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.

أداة 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

توضيح

Coreutils هو brew الحزمة التي تثبت أدوات GNU/Linux الأساسية التي تتوافق مع تطبيق Mac OSX لها بحيث يمكنك استخدامها

قد تجد برامج أو أدوات مساعدة على نظام mac osx الخاص بك والتي تبدو مشابهة لـ Linux coreutils ("الأدوات المساعدة الأساسية") ولكنها تختلف في بعض النواحي (مثل وجود علامات مختلفة).

وذلك لأن تطبيق Mac OSX لهذه الأدوات مختلف.للحصول على السلوك الأصلي المشابه لجنو/لينكس، يمكنك تثبيت coreutils الحزمة عبر brew نظام إدارة الحزم.

سيؤدي هذا إلى تثبيت الأدوات المساعدة الأساسية المقابلة، مسبوقة بـ g.على سبيل المثال.ل readlink, ، سوف تجد المقابل greadlink برنامج.

من أجل جعل readlink أداء مثل جنو readlink (greadlink) التنفيذ، يمكنك إنشاء اسم مستعار بسيط بعد تثبيت coreutils.

تطبيق

  1. تثبيت المشروب

اتبع التعليمات في https://brew.sh/

  1. قم بتثبيت حزمة coreutils

brew install coreutils

  1. إنشاء اسم مستعار

يمكنك وضع اسمك المستعار في ~/.bashrc، أو ~/.bash_profile، أو في أي مكان اعتدت فيه الاحتفاظ بأسماء bash المستعارة.أنا شخصياً أحتفظ بملفي في ~/.bashrc

alias readlink=greadlink

يمكنك إنشاء أسماء مستعارة مماثلة لبرامج أساسية أخرى مثل gmv، وgdu، وgdf، وما إلى ذلك.لكن احذر من أن سلوك GNU على جهاز Mac قد يكون مربكًا للآخرين المعتادين على العمل مع Coreutils الأصلية، أو قد يتصرف بطرق غير متوقعة على نظام Mac الخاص بك.

وهذا هو ما يمكنني استخدام:

وstat -f %N $your_path

والمسارات إلى readlink مختلفة بين نظام بلدي ولك. يرجى محاولة تحديد المسار الكامل:

<اقتباس فقرة>   

و/sw/sbin/readlink -f

وبيرل لديه وظيفة readlink (مثل كيف يمكنني نسخ الروابط رمزية في بيرل؟ ). هذا وتعمل في معظم المنصات، بما في ذلك 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 سميث يعطي حلقة لا نهائية.

وهنا هو جوابي، الذي استعمل فقط على صن أو. إس (صن أو. إس تفوت الكثير من 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"
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top