ฉันจะดูพฤติกรรมของ readlink ของ 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
นั่นอาจอธิบายความแตกต่าง ฉันค่อนข้างแน่ใจว่าฉันใช้ทุบตีทั้งสองครั้งแม้ว่า
troelskn

1
เหตุใดตัวเลือกนี้จึงผิดกฎหมายใน Macs
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. มันวนซ้ำตามลำดับของ symlink จนกระทั่งพบไฟล์จริง
  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

โปรดทราบว่านี่ไม่รวมถึงการจัดการข้อผิดพลาดใด ๆ สิ่งที่สำคัญเป็นพิเศษคือมันไม่ตรวจจับรอบ symlink วิธีง่าย ๆ ในการทำเช่นนี้คือการนับจำนวนครั้งที่คุณวนไปรอบ ๆ วงและล้มเหลวหากคุณกดจำนวนที่มากเกินไปเช่น 1,000

แก้ไขเพื่อใช้แทนpwd -P$PWD

โปรดทราบว่าสคริปต์นี้คาดว่าจะเรียกว่าชอบ./script_name filenameไม่-fเปลี่ยน$1ไป$2หากคุณต้องการที่จะใช้กับ-f filenamereadlink ของ GNU เช่น


1
เท่าที่ฉันสามารถบอกได้ว่ามันจะไม่ทำงานถ้าพ่อแม่ของเส้นทางเป็น symlink เช่น. ถ้าfoo -> /var/cuxไปแล้วfoo/barจะไม่ได้รับการแก้ไขเพราะbarไม่ได้มีการเชื่อมโยงถึงแม้จะfooมี
troelskn

1
อา. ใช่. มันไม่ง่าย แต่คุณสามารถอัปเดตสคริปต์ด้านบนเพื่อจัดการกับสิ่งนั้น ฉันจะแก้ไข (เขียนจริงๆ) คำตอบตามนั้น
Keith Smith

การเชื่อมโยงอาจอยู่ที่ใดก็ได้ในเส้นทาง ฉันเดาว่าสคริปต์สามารถทำซ้ำในส่วนต่าง ๆ ของเส้นทางได้ แต่มันก็ซับซ้อนไปหน่อย
troelskn

1
ขอบคุณ ปัญหาคือว่า $ PWD ให้ไดเรกทอรีการทำงานแบบลอจิคัลแก่เราโดยอ้างอิงจากค่าใน symlink ที่เราติดตาม เราสามารถหาไดเรกทอรีทางกายภาพจริงด้วย 'pwd -P' มันควรจะคำนวณโดยไล่ ".. " ถึงรากของระบบไฟล์ ฉันจะอัปเดตสคริปต์ในคำตอบของฉันตามนั้น
Keith Smith

12
ทางออกที่ดี แต่ก็ไม่ได้จัดการอย่างถูกต้องกับเส้นทางที่ต้องหลบหนีเช่นแฟ้มหรือไดเรกทอรีชื่อที่มีช่องว่างที่ฝังตัว เพื่อแก้ไขนั้นใช้cd "$(dirname "$TARGET_FILE")" และTARGET_FILE=$(readlink "$TARGET_FILE")และTARGET_FILE=$(basename "$TARGET_FILE")ในสถานที่ที่เหมาะสมในรหัสข้างต้น
mklement0

438

MacPorts และ Homebrew ให้แพ็คเกจcoreutilsที่มีgreadlink(GNU readlink) มอบเครดิตให้ Michael Kallweitt โพสต์ใน mackb.com

brew install coreutils

greadlink -f file.txt

หากคุณโทรหา readlink จากที่อื่นที่ไม่ใช่ bash โซลูชันอื่นจะลบ / usr / bin / readlink จากนั้นสร้างลิงก์ไปยัง / usr / local / bin / greadlink
chinglun

8
กรุณาอย่าเว้นแต่คุณก) เขียนซอฟต์แวร์เพื่อตัวคุณเอง 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
ผมอยากแอฟริกาและ utils อื่น ๆ ของฉัน Mac PATHที่ผมเชื่อว่าทำดีโดยไม่ต้องคำนำหน้าและสามารถเข้าถึงได้ง่ายจากผู้ใช้ของฉัน brew install <package> --default-namesไม่ได้ดังกล่าวข้างต้นที่เริ่มต้นด้วย หลายครั้งคำถามบนเว็บไซต์นี้ได้รับการตอบเล็กน้อยหรือมากกว่านั้นนอกเหนือจากข้อกำหนดทางเทคนิคของผู้ถาม แต่อยู่ในสถานการณ์ที่คล้ายกันและยังคงมีประโยชน์มาก topbug.net/blog/2013/04/14/…
Pysis

43

คุณอาจจะสนใจในหรืองูใหญ่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

ฉันรู้ว่าคุณบอกว่าคุณต้องการบางสิ่งที่เบากว่าภาษาสคริปต์อื่น แต่ในกรณีที่การรวบรวมไบนารีไม่สามารถทำได้คุณสามารถใช้ 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'
Wes Turner

@ โทรเลขฉันไม่ได้ตระหนักถึง Perl, Python และอื่น ๆ "อนุญาต" !! ... ในกรณีนี้ฉันจะเพิ่มperl -MCwd=abs_path -le 'print abs_path readlink(shift);'คำตอบของฉัน :-)
Cito

27

ฉันเกลียดไปกองกับการดำเนินงานยังอีก แต่ผมจำเป็นต้องมี) แบบพกพา, การดำเนินเปลือกบริสุทธิ์และ b) ความคุ้มครองหน่วยทดสอบเป็นจำนวนขอบกรณีสำหรับบางสิ่งบางอย่างเช่นนี้ไม่น่ารำคาญ

ดูโครงการของฉันบน Githubสำหรับการทดสอบและรหัสเต็ม สิ่งต่อไปนี้เป็นบทสรุปของการใช้งาน:

ในขณะที่ Keith Smith ชี้ให้เห็นอย่างชาญฉลาดreadlink -fทำสองสิ่ง: 1) แก้ไข symlink แบบวนซ้ำและ 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 
}

ทราบว่านี้เป็นรุ่นที่ง่ายขึ้นเล็กน้อยจากการดำเนินงานเต็มรูปแบบ การใช้งานเต็มรูปแบบเพิ่มการตรวจสอบขนาดเล็กสำหรับรอบ symlinkรวมถึงการนวดผลลัพธ์เล็กน้อย

ในที่สุดฟังก์ชั่นสำหรับ canonicalizing เส้นทาง:

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/เป็น/tmp/linkeddir/..ไม่ symlink แต่-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

จะมีการอ้างอิง dereference

การใช้งานในสคริปต์อาจเป็นเช่นนี้:

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

4
เพื่อรับบรรทัดใหม่ต่อท้ายเพิ่ม-lเช่นperl -MCwd -le 'print Cwd::abs_path shift' ~/non-absolute/file
pjvandehaar

26
  1. ติดตั้ง homebrew
  2. เรียกใช้ "ชงติดตั้ง coreutils"
  3. เรียกใช้ "เส้นทาง greadlink -f"

greadlink คือ readn gnu ที่ใช้ -f คุณสามารถใช้ macports หรือคนอื่น ๆ ได้เช่นกันฉันชอบ homebrew


2
ฉันไม่เห็นความแตกต่างใด ๆ กับคำตอบที่ได้รับในปี 2010 stackoverflow.com/a/4031502/695671
Jason S

หากคุณต้องการใช้คำสั่งเหล่านี้ด้วยชื่อปกติคุณสามารถเพิ่มไดเรกทอรี "gnubin" ใน PATH ของคุณจาก bashrc ของคุณเช่น: PATH = "/ usr / local / opt / coreutils / libexec / gnubin: $ PATH"
Denis Trofimov

20

ฉันสร้างสคริปต์ที่เรียกว่า realpath เป็นการส่วนตัวซึ่งมีลักษณะเล็ก ๆ น้อย ๆ เช่น:

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

คุณควรเปลี่ยนเป็นsys.argv[1]หากคุณต้องการให้สคริปต์พิมพ์ realpath ของอาร์กิวเมนต์แรก sys.argv[0]เพียงพิมพ์เส้นทางที่แท้จริงของสคริปต์ pyton เองซึ่งไม่มีประโยชน์มาก
Jakob Egger

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

สิ่งนี้ไม่สามารถใช้งานได้หากคุณมี symlink /usr/bin/เช่นalternativesระบบที่ต้องการ
akostadinov

ไม่ทำงานสำหรับไฟล์แต่ละไฟล์เฉพาะไดเรกทอรี GNU readlink ทำงานร่วมกับไดเรกทอรีไฟล์
symlink

7

นี่คือฟังก์ชั่นเชลล์แบบพกพาที่ควรทำงานในANY 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-clause (freeBSD style) สำเนาใบอนุญาตอาจพบได้โดยทำตามไฮเปอร์ลิงก์ข้างต้นไปยังเว็บไซต์ของฉัน


real_path ส่งคืน / ชื่อไฟล์แทน / path / to / filename บน Mac OS X Lion ใน bash shell

@ Barry เกิดขึ้นแบบเดียวกันกับ OSX Snow Leopard การโทรที่ต่อเนื่องกันไปยัง real_path () เอาท์พุทเรียงต่อกัน!
Dominique

@Dominique: ไม่น่าแปลกใจเนื่องจากฟังก์ชั่นภายในไม่ได้ทำงานใน subshell ของตัวเอง ... น่าเกลียดและเปราะบางแน่นอน
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 มาติดตั้งด้วย verions ล่าสุดperl) แล้ว:

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

จะทำงาน.


2
สิ่งนี้เป็นจริงใน FreeBSD 10 แต่ไม่ใช่ใน OS X 10.9
Zanchey

จับดี. สถิติหน้าคู่มืออ้างเป็นสำหรับ OS / X 10.9 Rตัวเลือกที่จะ-f%หายไป อาจstat -f%Yให้ผลลัพธ์ที่ต้องการ ฉันจะปรับคำตอบ โปรดทราบว่าstatเครื่องมือปรากฏใน FreeBSD ในรุ่น 4.10 และ NetBSD ในรุ่น 1.6
G. Cito

คำสั่ง Perl ทำงานไม่ถูกต้องใน 10.15 ให้มันเป็นไฟล์ในไดเรกทอรีปัจจุบันและมันจะให้ไดเรกทอรีปัจจุบัน (ไม่มีไฟล์)
codesniffer

7

วิธีที่ง่ายที่สุดในการแก้ปัญหานี้และเปิดใช้งานฟังก์ชันการทำงานของ readlink บน Mac ที่ติดตั้ง 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ค่อนข้างเรียบง่าย แต่มันหายไปท่ามกลางคำตอบอื่น ๆ รวมถึงคำตอบซ้ำดังนั้น downvote
Jason S

3

เริ่มอัปเดต

นี้เป็นเช่นปัญหาบ่อยที่เราได้ใส่กันทุบตี 4 ห้องสมุดสำหรับการใช้งานฟรี (MIT ใบอนุญาต) เรียกว่าrealpath-lib สิ่งนี้ได้รับการออกแบบมาเพื่อเลียนแบบreadlink -fโดยค่าเริ่มต้นและมีชุดทดสอบสองชุดเพื่อตรวจสอบ (1) ว่าใช้งานได้กับระบบยูนิกซ์ที่กำหนดและ (2) กับreadlink -fหากติดตั้ง (แต่ไม่จำเป็น) นอกจากนี้ยังสามารถใช้ในการตรวจสอบระบุและคลายลึก symlinks ที่ขาดและการอ้างอิงแบบวงกลมดังนั้นจึงเป็นเครื่องมือที่มีประโยชน์สำหรับการวินิจฉัยปัญหาไดเรกทอรีและฟิสิคัลที่ซ้อนกันเชิงลึกหรือเชิงสัญลักษณ์และไฟล์ มันสามารถพบได้ที่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ที่ให้ความสามารถในการแก้ไข symlink ไปยังระบบทางกายภาพ ตราบใดที่no_symlinksมีการตั้งค่าบางอย่างเช่นno_symlinks='on'symlink จะถูกแก้ไขในระบบทางกายภาพ มิฉะนั้นจะถูกนำไปใช้ (การตั้งค่าเริ่มต้น)

สิ่งนี้ควรใช้กับระบบใด ๆ ที่มี 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 ปัญหาคือการพึ่งพาและทุบตี Macs ไม่มีพวกเขาหรือถ้าพวกเขาทำพวกเขามักจะอยู่ที่อื่น (เส้นทางอื่น) การจัดการพา ธ ที่พึ่งพาในสคริปต์ Bash ข้ามแพลตฟอร์มนั้นน่าปวดหัวที่สุดและมีความเสี่ยงด้านความปลอดภัยที่เลวร้ายที่สุดดังนั้นจึงควรหลีกเลี่ยงการใช้งานหากเป็นไปได้

ฟังก์ชั่น get_realpath () ด้านล่างนั้นง่าย Bash-centric และไม่จำเป็นต้องพึ่งพา ฉันใช้ Bash builtins echoและcdเท่านั้น มีความปลอดภัยพอสมควรเนื่องจากทุกอย่างได้รับการทดสอบในแต่ละขั้นตอนและส่งคืนเงื่อนไขข้อผิดพลาด

หากคุณไม่ต้องการติดตาม symlinks ให้วางset -Pไว้ด้านหน้าสคริปต์ แต่มิฉะนั้นcdควรแก้ไข symlink ตามค่าเริ่มต้น ผ่านการทดสอบกับอาร์กิวเมนต์ไฟล์ที่เป็น {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+ แต่สามารถทำงานกับเวอร์ชันที่เก่ากว่าได้ถ้ามันยังอยู่


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 dependencies โดยอัตโนมัติ:

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 Xreadlink -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) ซึ่งเป็นคำถามที่เกี่ยวกับ
codesniffer

0

readlink -fการใช้งานที่สอดคล้องกับ POSIX สำหรับสคริปต์เชลล์ POSIX

https://github.com/ko1nksm/readlinkf

นี่เป็นไปตาม POSIX (ไม่มี bashism) โดยจะใช้ค่ามิได้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
อ๊ะ .. fink มีการแทนที่สำหรับreadlinkgnu ที่เข้ากันได้ เป็นเรื่องที่น่ารู้ แต่ก็ไม่ได้แก้ปัญหาของฉันเนื่องจากฉันต้องการสคริปต์ของฉันที่จะทำงานบนเครื่องประชาชนอื่น ๆ และฉันไม่ต้องการให้พวกเขาติดตั้ง fink สำหรับเรื่องนั้น
troelskn

คุณไม่ได้ตอบคำถามเดิม โปรดเขียนคำตอบของคุณอีกครั้ง
Jaime Hablutzel
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.