كيف يمكنني الحصول على سلوك readlink -f الخاص بـ GNU على جهاز Mac؟
سؤال
على لينكس، readlink
الأداة المساعدة تقبل خيارا -f
الذي يتبع روابط إضافية.يبدو أن هذا لا يعمل على أنظمة Mac وربما الأنظمة المستندة إلى BSD.ماذا سيكون المعادل؟
إليك بعض معلومات التصحيح:
$ which readlink; readlink -f
/usr/bin/readlink
readlink: illegal option -f
usage: readlink [-n] [file ...]
المحلول
readlink -f
يفعل شيئين:
- يتكرر على طول سلسلة من الارتباطات الرمزية حتى يعثر على الملف الفعلي.
- تقوم بإرجاع هذا الملف متعارف عليه الاسم - أي اسم المسار المطلق.
إذا كنت ترغب في ذلك، يمكنك فقط إنشاء برنامج نصي يستخدم سلوك رابط القراءة الفانيليا لتحقيق نفس الشيء.هنا مثال.من الواضح أنه يمكنك إدراج هذا في البرنامج النصي الخاص بك حيث تريد الاتصال به 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")
}
هذا كل شيء، أكثر أو أقل.بسيطة بما يكفي للصقها في البرنامج النصي الخاص بك، ولكنها صعبة بما يكفي لدرجة أنك ستصاب بالجنون عند الاعتماد على أي كود لا يحتوي على اختبارات وحدة لحالات الاستخدام الخاصة بك.
- تثبيت البيرة المنزلية
- قم بتشغيل "brew install coreutils"
- تشغيل "مسار 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.
تطبيق
- تثبيت المشروب
اتبع التعليمات في https://brew.sh/
- قم بتثبيت حزمة coreutils
brew install coreutils
- إنشاء اسم مستعار
يمكنك وضع اسمك المستعار في ~/.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"