Mac'te GNU'nun readlink -f davranışını nasıl edinebilirim?


377

Linux'ta, readlinkyardımcı program -fek bağlantıları izleyen bir seçeneği kabul eder . Bu Mac ve muhtemelen BSD tabanlı sistemlerde çalışmıyor gibi görünüyor. Eşdeğeri ne olurdu?

İşte bazı hata ayıklama bilgileri:

$ which readlink; readlink -f
/usr/bin/readlink
readlink: illegal option -f
usage: readlink [-n] [file ...]

1
Biraz geç, ancak sorunuz kullandığınız kabuktan bahsedilmiyor. Bu bir konuyla ilgilidir, çünkü readlinkbir yerleşik veya harici komut olabilir.
0xC0000022L

1
Bu belki de farkı açıklar. Her iki olayda da bash kullandığımdan oldukça eminim.
troelskn

1
Bu seçenek Mac'lerde neden yasa dışı?
CommaToast

3
Apple'ın OS X'i daha varsayılan linux yollarını desteklemesini ve bunun gibi şeyleri ele almasını isterdim. Hiçbir şey kırmadan yapabilirler, değil mi?
CommaToast

1
@CommaToast Peki, Perl so ++ :-) ... ile açın touch myfile ; ln -s myfile otherfile ; perl -MCwd=abs_path -le 'print abs_path readlink(shift);' otherfile. Aşağıdaki yanıtıma ekledim. Şerefe.
G. Cito

Yanıtlar:


181

readlink -f iki şey yapar:

  1. Gerçek bir dosya bulana kadar bir dizi sembol boyunca yinelenir.
  2. Dosyanın standartlaştırılmış adını (yani, mutlak yol adını) döndürür .

İsterseniz, aynı şeyi elde etmek için vanilya okuma bağlantısı davranışını kullanan bir kabuk betiği oluşturabilirsiniz. İşte bir örnek. Açıkçası bunu kendi betiğinize aramak istediğiniz yere ekleyebilirsinizreadlink -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

Bunun herhangi bir hata işlemeyi içermediğini unutmayın. Özellikle önemli, sembolik döngüleri algılamaz. Bunu yapmanın basit bir yolu, döngü gibi gittiğiniz ve 1000 gibi çok büyük bir sayıya çarptığınızda başarısız olduğunuz sayıları saymak olacaktır.

Kullanmak DÜZENLENMİŞ pwd -Pyerine $PWD.

Bu komut beklediği gibi çağrılacak o Not ./script_name filenamehayır, -fdeğiştirin $1etmek $2isterseniz ile kullanmak mümkün -f filenameGNU readlink gibi.


1
Anlayabildiğim kadarıyla, yolun üst dizini bir sembolik bağlantıysa, bu çalışmaz. Örneğin. eğer foo -> /var/cux, o foo/barzaman çözülmeyecek, çünkü barbir bağlantı değil foo.
troelskn

1
Ah. Evet. Bu kadar basit değil, ancak bununla başa çıkmak için yukarıdaki komut dosyasını güncelleyebilirsiniz. Cevabı buna göre düzenleyeceğim (gerçekten yeniden yazacağım).
Keith Smith

Bir bağlantı yolun herhangi bir yerinde olabilir. Senaryo yolun her parçası üzerinde yineleme olabilir, ama o zaman biraz karmaşık hale gelir.
troelskn

1
Teşekkürler. Sorun şu ki, $ PWD bize takip ettiğimiz sembolik değerlere dayanarak bize mantıksal çalışma dizinini veriyor. Gerçek fiziksel dizini 'pwd -P' ile alabiliriz. Dosya sisteminin kök dizinine kadar “..” peşinde koşarak onu hesaplamalıyız. Cevabımdaki betiği buna göre güncelleyeceğim.
Keith Smith

12
Harika bir çözüm, ancak gömülü boşluklu dosya veya dizin adları gibi kaçması gereken yollarla düzgün bir şekilde ilgilenmiyor . Bunu düzeltmek için yukarıdaki kodda ve ve uygun yerlerde kullanın . cd "$(dirname "$TARGET_FILE")"TARGET_FILE=$(readlink "$TARGET_FILE")TARGET_FILE=$(basename "$TARGET_FILE")
mklement0

438

MacPorts ve Homebrew, (GNU readlink) içeren bir coreutils paketi sağlar greadlink. Mackb.com'daki Michael Kallweitt yazısına teşekkür ederiz.

brew install coreutils

greadlink -f file.txt

Eğer bash dışındaki yerlerden readlink çağırıyorsanız, başka bir çözüm / usr / bin / readlink'i kaldırmak ve / usr / local / bin / greadlink için bir bağlantı oluşturmak olacaktır.
chinglun

8
A) kendiniz için yazılım yazmadığınız sürece lütfen b) başkaları ile uğraşmak istemeyin. Bunu herkes için "işe yarayacak" şekilde yazın.
kgadek

14
@ching Bunu sizin .bashrcexport PATH="/usr/local/opt/coreutils/libexec/gnubin:$PATH"
yerinizde

1
Soru OS X veya BSD sistemindeki eşdeğerini soruyor, bu cevap vermiyor. Ayrıca Homebrew'u da kurmanız gerekecek. Ayrıca GNU Readlink dışında birçok şey yükleyeceksiniz. Standart OS X yüklemeleri için bir komut dosyası yazıyorsanız, bu yanıt size yardımcı olmaz. Herhangi bir şey yüklemeden OS X'te pwd -Pve readlinküzerinde bir kombinasyon kullanabilirsiniz
Jason S

4
Mac bilgisayarımdaki GNU ve diğer araçların öneki olmadan ve kullanıcımdan kolayca erişilebildiğini düşündüğümü istedimPATH . Yukarıdaki ile başladı brew install <package> --default-names. Çoğu zaman bu sitedeki bir soru, askerin teknik gereksinimlerinin dışında, ancak benzer bir durumda, hafif bir şekilde veya daha fazla cevaplanmıştır ve hala çok yardımcı olmuştur. topbug.net/blog/2013/04/14/…
Pysis

43

İlginizi çekebilir realpath(3)veya Python'lar os.path.realpath. İkisi tam olarak aynı değil; C kütüphanesi çağrısı, Python sürümü mevcut değilken ara yol bileşenlerinin var olmasını gerektirir.

$ 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

Başka bir komut dosyası dilinden daha hafif bir şey tercih edeceğinizi söylediğinizi biliyorum, ancak bir ikili derlemenin yetersiz olması durumunda, kitaplık çağrısını sarmak için Python ve ctypes (Mac OS X 10.5'te mevcut) kullanabilirsiniz:

#!/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])

İronik olarak, bu komut dosyasının C versiyonu daha kısa olmalıdır. :)


Evet, realpathgerçekten istediğim bu. Ama bu işlevi bir kabuk komut dosyasından almak için bir ikili derlemek zorunda oldukça garip görünüyor.
troelskn

4
Peki neden Python tek satırını shell betiğinde kullanmıyorsunuz? (Tek hatlı bir görüşmeden readlinkkendisine çok farklı değil , değil mi?)
Telemachus

3
python -c "import os,sys; print(os.path.realpath(os.path.expanduser(sys.argv[1])))" "${1}"gibi yollar ile çalışır'~/.symlink'
Wes Turner

@troelskin Perl, Python, vb "izin" farkında değildi ! ... bu durumda perl -MCwd=abs_path -le 'print abs_path readlink(shift);'cevabımı ekleyeceğim :-)
G. Cito

27

Yine başka bir uygulama ile uğraşmaktan nefret ediyorum, ancak a) taşınabilir, saf bir kabuk uygulamasına ve b) birim test kapsamına ihtiyacım vardı , çünkü böyle bir şey için uç vakaların sayısı önemsiz değildir .

Testler ve tam kod için Github'daki projeme bakın . Aşağıdakiler uygulamanın bir özetidir:

Keith Smith'in açıkça belirttiği gibi readlink -f, iki şey yapar: 1) sembolik bağlantıları yinelemeli olarak çözer ve 2) sonucu kanonikleştirir, dolayısıyla:

realpath() {
    canonicalize_path "$(resolve_symlinks "$1")"
}

İlk olarak, symlink çözümleyici uygulaması:

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 
}

Bunun tam uygulamanın biraz basitleştirilmiş bir sürümü olduğunu unutmayın . Tam uygulama, sembolik bağlantı döngüleri için küçük bir kontrol ekler , ayrıca çıktıya biraz masaj yapar.

Son olarak, bir yolu standartlaştırma işlevi:

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

Aşağı yukarı bu kadar. Betiğinize yapıştıracak kadar basit, ancak kullanım durumlarınız için birim testleri olmayan herhangi bir koda güvenmek için çılgın olacağınız kadar zor.


3
Bu POSIX değil - POSIX olmayan localanahtar kelimeyi kullanıyor
go2null

Bu readlink -fne ile ne de eşdeğerdir realpath. Coreutils formülünün yüklü olduğu ( greadlinkvarsa) bir Mac varsayalım ln -s /tmp/linkeddir ~/Documents; greadlink -f /tmp/linkeddir/..ana dizininizi yazdırır ( üst dir ref'yi /tmp/linkeddiruygulamadan önce bağlantıyı ..çözer), ancak kodunuz bir sembolik bağlantı /private/tmp/olarak /tmp/linkeddir/..üretilmez, ancak -d /tmp/linkeddir/..doğrudur ve cd /tmp/linkeddir/..sizi /tmpkanonik yolu olan yönlendirir /private/tmp. Kodunuz realpath -Lbunun yerine ne yapar.
Martijn Pieters

27

Herhangi bir harici bağımlılık olmadan neredeyse her yerde çalışacağından emin olan basit bir tek astarlı:

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

Dereference sembolleri olacaktır.

Bir komut dosyasında kullanım şöyle olabilir:

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

4
sondaki bir yeni satır almak, eklemek -l, gibiperl -MCwd -le 'print Cwd::abs_path shift' ~/non-absolute/file
pjvandehaar

26
  1. Homebrew yükleyin
  2. "Demlemek yükleme coreutils" çalıştırın
  3. "Greadlink -f yolunu" çalıştır

greadlink, -f uygulayan gnu readlink'idir. Macport veya başkalarını da kullanabilirsiniz, ben homebrew tercih ederim.


2
2010 yılında verilen cevapta herhangi bir fark göremiyorum stackoverflow.com/a/4031502/695671
Jason S

Bu komutları normal adlarıyla kullanmanız gerekirse, PATH'nize bashrc'nizden bir "gnubin" dizini ekleyebilirsiniz: PATH = "/ usr / local / opt / coreutils / libexec / gnubin: $ PATH"
Denis Trofimov

20

Ben şahsen realpath adlı bir senaryo yaptım ki bu gibi bir şey gibi görünüyor:

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

sys.argv[1]Komut dosyasının ilk argümanın realpath'ını yazdırmasını istiyorsanız bunu olarak değiştirmeniz gerekir . sys.argv[0]sadece çok yararlı olmayan pyton betiğinin gerçek yolunu yazdırır.
Jakob Egger

17
İşte takma ad sürümü ~/.profile:alias realpath="python -c 'import os, sys; print os.path.realpath(sys.argv[1])'"
jwhitlock

Harika cevap ve harika bir liner @jwhitlock. readlink -fOP'den beri, olası -fbayrağı desteklemek için @jwhitlock takma adının değiştirilmiş bir sürümü : 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

Peki buna ne dersin?

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

Benim için çalışan tek çözüm bu; Ben gibi yollar ayrıştırmak gerekir ../../../->/
keflavich

/usr/bin/örneğin alternativessistemin sahip olması gibi bir symlink'iniz varsa bu çalışmaz .
akostadinov

Tek tek dosyalar için çalışmaz, sadece dizinler. GNU readlink dizinler, dosyalar,
semboller

7

İşte HERHANGİ BİR Bourne karşılaştırılabilir kabukta çalışması gereken taşınabilir bir kabuk işlevi . Göreli yol noktalama işaretini ".. veya." ve dereference sembolik bağlantıları.

Herhangi bir nedenle realpath (1) komutuna veya readlink (1) komutuna sahip değilseniz, bu diğer ad olarak adlandırılabilir.

which realpath || alias realpath='real_path'

Zevk almak:

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

Ayrıca, herhangi birinin burada ilgilenmesi durumunda,% 100 saf kabuk kodunda basename ve dirname'nin nasıl uygulanacağı:

## 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##*/}"
}

Bu kabuk kodunun güncellenmiş sürümünü google sitemde bulabilirsiniz: http://sites.google.com/site/jdisnard/realpath

DÜZENLEME: Bu kod, 2-koşulu (freeBSD stili) lisans koşulları kapsamında lisanslanmıştır. Lisansın bir kopyası, siteme yukarıdaki köprü izlenerek bulunabilir.


Real_path, bash kabuğundaki Mac OS X Lion'da / path / to / dosyaadı yerine / dosyaadı döndürür.

@Barry Aynı şey OSX Snow Leopard'da da olur. Daha kötü, arka arkaya real_path () çağrıları bitiştir çıktı!
Dominique

@Dominique: şaşırtıcı değil, çünkü fonksiyonun içi kendi alt kabuğunda çalışmaz ... çirkin ve kırılgan.
0xC0000022L

7

FreeBSD ve OSXstat , NetBSD'den türetilmiş bir versiyona sahiptir .

Çıkışı format anahtarlarıyla ayarlayabilirsiniz (yukarıdaki bağlantılarda bulunan manuel sayfalara bakın).

%  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

Bazı OS X sürümleri stat olmayabilir -f%Rbiçimleri için bir seçenek. Bu durumda -stat -f%Yyeterli olabilir. -f%YSeçenek ise bir sembolik bağın hedefi, gösterecektir -f%Rmutlak yoludur dosyasına karşılık gelen gösterir.

DÜZENLE:

Perl'i kullanabiliyorsanız (Darwin / OS X, son sürümlerle birlikte gelir perl)

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

çalışacak.


2
Bu FreeBSD 10 için geçerlidir ancak OS X 10.9 için geçerli değildir.
Zanchey

İyi yakalama. Belirtilen stat manuel sayfası OS / X 10.9 içindir. RSeçeneği için -f%eksik. Muhtemelen stat -f%Yistenen çıktıyı verir. Cevabı ayarlayacağım. statAracın 4.10 sürümünde FreeBSD ve 1.6 sürümünde NetBSD'de göründüğünü unutmayın .
G. Cito

Perl komutu 10.15'te düzgün çalışmıyor. Geçerli dizinde bir dosya verin ve geçerli dizini (dosya olmadan) verir.
codesniffer

7

Bu sorunu çözmenin ve yüklü Homebrew veya FreeBSD ile Mac'te readlink'in işlevselliğini etkinleştirmenin en kolay yolu 'coreutils' paketini yüklemektir. Bazı Linux dağıtımlarında ve diğer POSIX işletim sistemlerinde de gerekli olabilir.

Örneğin, FreeBSD 11'de şunları çağırarak yükledim:

# pkg install coreutils

Homebrew ile MacOS'ta komut şu şekilde olur:

$ brew install coreutils

Diğer cevapların neden bu kadar karmaşık olduğundan emin değilim, hepsi bu kadar. Dosyalar farklı bir yerde değil, henüz yüklenmemişler.


7
brew install coreutils --with-default-namesdaha spesifik olmak. Veya bunun yerine "greadlink" ile sonuçlanacaksınız ...
Henk

2010 stackoverflow.com/a/4031502/695671 ve 2012 stackoverflow.com/a/9918368/695671 yanıtlarının hiçbir farkı göremiyorum . Soru aslında Mac'te ve muhtemelen BSD tabanlı sistemlerde. eşdeğer olur mu? " Bunun bu soruya çok iyi bir cevap olduğuna inanmıyorum. Birçok neden, ancak hiçbir şey yükleme yeteneği olmadan Mac OS makinelerini hedefliyor olabilirsiniz. pwd -Poldukça basittir, ancak tekrarlananlar da dahil olmak üzere diğer birçok cevap arasında kaybolur.
Jason S

3

Güncellemeye Başla

Bu, sık kullanılan bir problemdir ve realpath-lib adlı ücretsiz kullanım için bir Bash 4 kütüphanesini (MIT Lisansı) bir araya getirdik . Bu, varsayılan olarak readlink -f öykünmesi için tasarlanmıştır ve (1) belirli bir unix sistemi için çalıştığını ve (2) yüklüyse readlink -f'ye karşı çalıştığını doğrulamak için iki test paketi içerir (ancak bu gerekli değildir). Ek olarak, derin, kırık semboller ve dairesel referansları araştırmak, tanımlamak ve çözmek için kullanılabilir, bu nedenle derin yuvalanmış fiziksel veya sembolik dizin ve dosya sorunlarını teşhis etmek için yararlı bir araç olabilir. Bu bulunabilir github.com veya bitbucket.org .

Güncellemeyi Sonlandır

Bash dışında hiçbir şeye dayanmayan çok kompakt ve verimli bir başka çözüm:

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

}

Bu no_symlinks, fiziksel sisteme sembolik bağlantıları çözme olanağı sağlayan bir ortam ayarı da içerir . Bir no_symlinksşeye ayarlandığı sürece , yani no_symlinks='on'semboller fiziksel sisteme çözülecektir. Aksi takdirde uygulanırlar (varsayılan ayar).

Bu, Bash sağlayan herhangi bir sistemde çalışmalı ve test amacıyla Bash uyumlu bir çıkış kodu döndürecektir.


3

Zaten çok fazla cevap var, ama hiçbiri benim için işe yaramadı ... Yani şimdi bunu kullanıyorum.

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
Bu bir yolsa işe yaramaz $target, yalnızca dosya ise işe yarar.
MaxGhost

3

Benim için çalışan tembel bir yol,

$ brew install coreutils
$ ln -s /usr/local/bin/greadlink /usr/local/bin/readlink
$ which readlink
/usr/local/bin/readlink
/usr/bin/readlink

1

Her zamankinden daha iyi geç, sanırım. Bunu özellikle geliştirmek için motive oldum çünkü Fedora scriptlerim Mac üzerinde çalışmıyordu. Sorun bağımlılıklar ve Bash. Mac'lerde yoktur veya varsa, genellikle başka bir yerdedir (başka bir yol). Bir platformlar arası Bash betiğinde bağımlılık yolu manipülasyonu en iyi ihtimalle baş ağrısı ve en kötü ihtimalle güvenlik riskidir - bu nedenle mümkünse kullanımlarından kaçınmak en iyisidir.

Aşağıdaki get_realpath () işlevi basittir, Bash merkezlidir ve bağımlılık gerekmez. Sadece Bash yerleşiklerini yankı ve cd kullanıyorum . Her şey yolun her aşamasında test edildiğinden ve hata koşullarını döndürdüğü için de oldukça güvenlidir.

Symlinks'i takip etmek istemiyorsanız, set -P'yi komut dosyasının önüne koyun , aksi takdirde cd , sembolik bağlantıları varsayılan olarak çözmelidir. {Mutlak | göreli | sembolik | local} ve dosyanın mutlak yolunu döndürür. Şimdiye kadar herhangi bir sorun yaşamadık.

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

}

Bunu get_dirname, get_filename, get_stemname ve validate_path işlevleriyle birleştirebilirsiniz. Bunlar GitHub depomuzda realpath-lib olarak bulunabilir (tam açıklama - bu bizim ürünümüzdür, ancak herhangi bir kısıtlama olmaksızın topluluğa ücretsiz sunuyoruz). Aynı zamanda bir öğretim aracı olarak da kullanılabilir - iyi belgelenmiştir.

Sözde 'modern Bash' uygulamalarını uygulamak için elimizden gelenin en iyisini yaptık, ancak Bash büyük bir konudur ve her zaman iyileştirme için yer olacağından eminim. Bash 4+ gerektirir, ancak hala etraftalarsa eski sürümlerle çalışmak için yapılabilir.


1

Çalışmam BSD olmayan Linux ve macOS tarafından kullanıldığından, bu takma adları yapı komut dosyalarımızda kullanmayı tercih ettim ( sedbenzer sorunları olduğu için dahil):

##
# 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 bağımlılıklarını otomatik olarak yüklemek için ekleyebileceğiniz isteğe bağlı kontrol :

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

Gerçekten "küresel" sanırım başkalarını kontrol etmek gerekiyor ... ama bu muhtemelen 80/20 işareti yakın geliyor.


1

açıklama

coreutils, brewMac OSX uygulamasına karşılık gelen GNU / Linux çekirdek yardımcı programlarını yükleyen bir pakettir.

Mac osx sisteminizde Linux coreutils'e ("Core Utilities") benzeyen ancak bazı şekillerde farklılık gösteren (farklı bayraklar gibi) programlar veya araçlar bulabilirsiniz.

Bunun nedeni, bu araçların Mac OSX uygulamasının farklı olmasıdır. Orijinal GNU / Linux benzeri davranışı elde etmek için coreutilspaketi brewpaket yönetim sistemi üzerinden yükleyebilirsiniz .

Bu, öneki olan karşılık gelen çekirdek yardımcı programları yükleyecektir g. Örneğin readlink, ilgili bir greadlinkprogram bulacaksınız .

Yapmak için readlinkGNU gibi gerçekleştirmek readlink( greadlinkEğer coreutils yükledikten sonra) uygulanması, basit bir takma yapabilirsiniz.

uygulama

  1. Demlemek yükleyin

Https://brew.sh/ adresindeki talimatları izleyin.

  1. Coreutils paketini yükleyin

brew install coreutils

  1. Takma Ad Oluşturma

Takma adınızı ~ / .bashrc, ~ / .bash_profile klasörüne veya bash takma adlarınızı tutmak için kullandığınız herhangi bir yere yerleştirebilirsiniz. Şahsen benimkini ~ / .bashrc içinde tutuyorum

alias readlink=greadlink

Gmv, gdu, gdf ve benzeri diğer çekirdek sunucular için benzer takma adlar oluşturabilirsiniz. Ancak, bir mac makinesindeki GNU davranışının, yerel coreutils ile çalışmaya alışkın olanlar için kafa karıştırıcı olabileceğini veya mac sisteminizde beklenmedik şekillerde davranabileceğini unutmayın.


0

OS X için aynı sonuçları sağlayabilen bir realpath yardımcı programı yazdım readlink -f.


İşte bir örnek:

(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


Eğer MacPorts kullanıyorsanız, aşağıdaki komutu kullanarak yükleyebilirsiniz: sudo port selfupdate && sudo port install realpath.


0

Gerçekten platforma bağımlı bu R-lineer olacaktır

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

Aslında taklit etmek için readlink -f <path>1 dolar yerine 2 dolar kullanılmalıdır.


R hariç, varsayılan olarak herhangi bir sistemde neredeyse hiç yoktur. Henüz Mac'te değil (10.15 itibariyle) bu sorunun ne olduğudur.
codesniffer

0

POSIX readlink -fkabuk komut dosyaları için POSIX uyumlu uygulama

https://github.com/ko1nksm/readlinkf

Bu POSIX uyumludur (bashizm yok). Ne ne readlinkde kullanır realpath. GNU ile kıyaslayarak tam olarak aynı olduğunu doğruladım readlink -f( test sonuçlarına bakın ). Hata işleme ve iyi performansa sahiptir. 'Dan güvenle değiştirebilirsiniz readlink -f. Lisans CC0'dır, bu yüzden herhangi bir proje için kullanabilirsiniz.

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

Lütfen en son koda bakın. Bazıları düzeltilebilir.


-2

Perl'in bir readlink işlevi vardır (örn . Perl'deki sembolik bağlantıları nasıl kopyalarım? ). Bu, OS X dahil çoğu platformda çalışır:

perl -e "print readlink '/path/to/link'"

Örneğin:

$ mkdir -p a/b/c
$ ln -s a/b/c x
$ perl -e "print readlink 'x'"
a/b/c

5
Bu readlink, -fseçenek olmadan aynı şeyi yapar - özyineleme yok, mutlak yol yok - bu yüzden readlinkdoğrudan da kullanabilirsiniz .
mklement0

-2

@ Keith Smith'in cevabı sonsuz bir döngü veriyor.

Sadece SunOS'ta kullandığım cevabım (SunOS çok fazla POSIX ve GNU komutunu özlüyor).

$ PATH dizinlerinden birine koymanız gereken bir komut dosyası:

#!/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'e giden yollar sistemim ile sizinki arasında farklı. Lütfen tam yolu belirtmeyi deneyin:

/sw/sbin/readlink -f


1
Ve / tam olarak - / sw / sbin ve / usr / bin arasındaki fark nedir? Ve iki ikili neden farklı?
troelskn

4
Aha .. bu yüzden fink readlinkgnu uyumlu bir yedek içerir . Bunu bilmek güzel, ama sorunumu çözmedi, çünkü betiğimin diğer halklar makinesinde çalışması için ihtiyacım var ve bunun için fink yüklemelerini gerektiremem.
troelskn

Orijinal soruyu cevaplamıyorsunuz. Lütfen cevabınızı tekrar yazın.
Jaime Hablutzel
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.