Kabuk betiğindeki sembolik bağlantılar nasıl giderilir


220

(Unix benzeri bir sistemde) mutlak veya göreceli bir yol göz önüne alındığında, herhangi bir ara bağlantıyı çözdükten sonra hedefin tam yolunu belirlemek istiyorum. Aynı zamanda ~ kullanıcı adı gösterimini çözmek için bonus puanlar.

Hedef bir dizin ise, dizine chdir () ve sonra getcwd () çağırmak mümkün olabilir, ama gerçekten bunu bir C yardımcısı yazmak yerine bir kabuk komut dosyasından yapmak istiyorum. Ne yazık ki, mermilerin kullanıcıdan linklerin varlığını saklamaya eğilimi vardır (bu OS X'te bash'tır):

$ ls -ld foo bar
drwxr-xr-x   2 greg  greg  68 Aug 11 22:36 bar
lrwxr-xr-x   1 greg  greg   3 Aug 11 22:36 foo -> bar
$ cd foo
$ pwd
/Users/greg/tmp/foo
$

Ne istiyorum yukarıdaki örnekteki tmp dizininden yürütüldüğünde, çözmek ("foo") == "/ Users / greg / tmp / bar" gibi bir işlev resol () 'dir.

Yanıtlar:


92

Standartlara göre, pwd -Psemboller çözülmüş yolu ile dönmelidir.

C fonksiyonu char *getcwd(char *buf, size_t size)gelen unistd.haynı davranışı olmalıdır.

getcwd pwd


22
Bu sadece (geçerli) dizin için çalışır? Hedef bir
dosyaysa

4
Bağlantı koptuğunda çalışmaz, çünkü mevcut yolunuz olamaz
Tom Howard

6
Gezin faydasıyla özetlemek gerekirse: Bu cevap yalnızca çok sınırlı durumlarda, yani ilgilenilen bağlantının gerçekten var olan bir dizine ait olması durumunda çalışır ; artı, cdaramadan önce bunu yapmanız gerekir pwd -P. Başka bir deyişle: o (hedef bakınız) sembolik bağların kararlılığının sizi izin vermez dosyalara veya kırılmış ve ek iş yapmak zorunda varolan dizin sembolik çözülmesi için sembolik bağı (önceki çalışma dir geri yüklemek veya lokalize cdve pwd -Paramaları bir alt kabukta).
mklement0

kötü bir dizin değil, bir dosyayı çözmek için bir yol arıyorum.
Martin

Diğerlerinin de belirttiği gibi, bu soruya gerçekten cevap vermiyor. @ pixelbeat'ın cevabı aşağıda.
erstaples

402
readlink -f "$path"

Editörün notu: Yukarıdakiler GNU readlink ve FreeBSD / PC-BSD / OpenBSD ile çalışır readlink , ancak 10.11'den itibaren OS X üzerinde çalışmaz .
GNU , nihai hedefin mevcut olup olmadığı konusunda bir sembolik bağlantıyı çözmek için readlinkek, ilgili seçenekler sunar -m.

GNU coreutils 8.15 (2012-01-06) ' dan bu yana, yukarıdakilerden daha az geniş ve daha esnek bir realpath programı mevcuttur. Aynı adı taşıyan FreeBSD programı ile de uyumludur. Ayrıca iki dosya arasında göreceli bir yol oluşturmak için işlevsellik de içerir.

realpath $path

[ Halloleo - danorton tarafından yapılan yorumdan aşağıda yönetici ekleme ]

(En azından 10.11.x aracılığıyla) Mac OS X, kullanım için readlinkolmadan -fseçeneği:

readlink $path

Editörün notu: Bu sembolik bağlantıları özyinelemeli ve böylece nihai hedefi bildirmeyecektir ; örneğin, aişaret eden sembolik işaret b, bu da işaret eder c, bu yalnızca rapor verir b(ve mutlak bir yol olarak çıktı alınmasını sağlamaz ). Eksik işlevsellik boşluğunu doldurmak için OS X'te
aşağıdaki perlkomutu kullanın readlink -f:
perl -MCwd -le 'print Cwd::abs_path(shift)' "$path"


5
Bu Mac OS X'te çalışmaz - bkz. Stackoverflow.com/questions/1055671/…
Bkkbrad

1
OS X uyumsuzluğu hakkında utanç, aksi halde güzel +1
jkp

11
OS X'te homutrew ile coreutils kurabilirsiniz. "Grealpath" olarak kurar.
Kief

10
readlinkOSX üzerinde çalışır, ancak başka sözdizimi gerekir: readlink $path olmadan-f .
halloleo

2
readlink, birden çok symlink katmanının referansını kaldıramıyor, her seferinde bir katman siliyor
Magnus

26

Eğer sadece dizini istiyorsanız "pwd -P" çalışıyor gibi görünse de, herhangi bir nedenden dolayı gerçek çalıştırılabilir dosyanın ismini istiyorsanız bunun yardımcı olduğunu düşünmüyorum. İşte benim çözümüm:

#!/bin/bash

# get the absolute path of the executable
SELF_PATH=$(cd -P -- "$(dirname -- "$0")" && pwd -P) && SELF_PATH=$SELF_PATH/$(basename -- "$0")

# resolve symlinks
while [[ -h $SELF_PATH ]]; do
    # 1) cd to directory of the symlink
    # 2) cd to the directory of where the symlink points
    # 3) get the pwd
    # 4) append the basename
    DIR=$(dirname -- "$SELF_PATH")
    SYM=$(readlink "$SELF_PATH")
    SELF_PATH=$(cd "$DIR" && cd "$(dirname -- "$SYM")" && pwd)/$(basename -- "$SYM")
done

17

Favorilerimden biri realpath foo

realpath - standartlaştırılmış mutlak yol adını döndürür

realpath, tüm sembolik bağlantıları genişletir ve yol ile adlandırılan boş sonlandırılmış dizede '/./', '/../' ve ekstra '/' karakterlerine başvuruları çözer ve
       standartlaştırılmış mutlak yol adını resolved_path tarafından adlandırılan PATH_MAX boyutundaki arabellekte saklar. Ortaya çıkan yolun sembolik bağlantısı olmayacak, '/./' veya
       '/../' bileşenleri.

Debian'da (etch ve üstü) bu komut realpath paketinde bulunur.
Phil Ross

2
realpath artık (Ocak 2012) coreutils'in bir parçası ve debian ve BSD varyantı ile geriye dönük uyumlu
pixelbeat

1
Ben yok realpathGNU 8.4.31 coreutils ile CentOS 6. Ben birkaç diğerleri rastlamak ettik Unix ve Linux paketlenmiş bir GNU coreutils sahip olmadan realpath . Yani sadece versiyondan daha fazlasına bağımlı gibi görünüyor.
Mart'ta toxalot

Tercihim realpathüzerinde readlinko gibi seçenek bayraklarını sunmaktadır çünkü--relative-to
arr_sea

10
readlink -e [filepath]

tam olarak istediğin gibi görünüyor - bir arbirary yolunu kabul ediyor, tüm simgeleri çözüyor ve "gerçek" yolu döndürüyor - ve muhtemelen tüm sistemlerin zaten sahip olduğu "standart * nix"


Sadece ısırıldı. Mac'te çalışmıyor ve değiştirme için arıyorum.
dhill

5

Diğer yol:

# Gets the real path of a link, following all links
myreadlink() { [ ! -h "$1" ] && echo "$1" || (local link="$(expr "$(command ls -ld -- "$1")" : '.*-> \(.*\)$')"; cd $(dirname $1); myreadlink "$link" | sed "s|^\([^/].*\)\$|$(dirname $1)/\1|"); }

# Returns the absolute path to a command, maybe in $PATH (which) or not. If not found, returns the same
whereis() { echo $1 | sed "s|^\([^/].*/.*\)|$(pwd)/\1|;s|^\([^/]*\)$|$(which -- $1)|;s|^$|$1|"; } 

# Returns the realpath of a called command.
whereis_realpath() { local SCRIPT_PATH=$(whereis $1); myreadlink ${SCRIPT_PATH} | sed "s|^\([^/].*\)\$|$(dirname ${SCRIPT_PATH})/\1|"; } 

Myreadlink () cd gerekir, çünkü bir bağlantı bulana kadar her dizine gidiş, özyinelemeli bir işlevdir. Bir bağlantı bulursa, realpath öğesini döndürür ve sed yolun yerini alır.
Keymon

5

Verilen çözümlerden bazılarını bir araya getirmek, readlink'in çoğu sistemde mevcut olduğunu ancak farklı argümanlara ihtiyaç duyduğunu bilmek, bu benim için OSX ve Debian'da işe yarar. BSD sistemlerinden emin değilim. Belki de durumun yalnızca OSX'ten [[ $OSTYPE != darwin* ]]dışlanması gerekir -f.

#!/bin/bash
MY_DIR=$( cd $(dirname $(readlink `[[ $OSTYPE == linux* ]] && echo "-f"` $0)) ; pwd -P)
echo "$MY_DIR"

3

MacOS / Unix'teki dosyanın gerçek yolunu bir satır içi Perl betiği kullanarak şu şekilde edinebilirsiniz:

FILE=$(perl -e "use Cwd qw(abs_path); print abs_path('$0')")

Benzer şekilde, sembolik bir dosyanın dizinini almak için:

DIR=$(perl -e "use Cwd qw(abs_path); use File::Basename; print dirname(abs_path('$0'))")

3

Yolunuz bir dizin mi, yoksa bir dosya olabilir mi? Bir dizinse, basit:

(cd "$DIR"; pwd -P)

Ancak, bir dosya olabilirse, bu çalışmaz:

DIR=$(cd $(dirname "$FILE"); pwd -P); echo "${DIR}/$(readlink "$FILE")"

çünkü sembolik bağıl veya tam bir yola çözümlenebilir.

Komut dosyalarında gerçek yolu bulmam gerekiyor, böylece yapılandırmayı veya onunla birlikte yüklenen diğer komut dosyalarını referans gösterebilirim, bunu kullanıyorum:

SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
  SOURCE="$(readlink "$SOURCE")"
  [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done

SOURCEHerhangi bir dosya yoluna ayarlayabilirsiniz . Temel olarak, yol bir sembolik bağlantı olduğu sürece, o sembolik bağlantıyı çözer. Hile, döngünün son satırında. Çözümlenmiş sembolik bağlantı mutlaksa, bunu olarak kullanır SOURCE. Bununla birlikte, göreceli ise, DIRilk olarak tarif ettiğim basit hile ile gerçek bir konuma çözülen bunun için başlayacaktır .


2
function realpath {
    local r=$1; local t=$(readlink $r)
    while [ $t ]; do
        r=$(cd $(dirname $r) && cd $(dirname $t) && pwd -P)/$(basename $t)
        t=$(readlink $r)
    done
    echo $r
}

#example usage
SCRIPT_PARENT_DIR=$(dirname $(realpath "$0"))/..

Bu, (a) boşluk veya kabuk metakarakterleri ve (b) bozuk semboller içeren herhangi bir yolla kesilir (yalnızca çalışan komut dosyasının üst yolunu istiyorsanız, (a) geçerli olmadığını varsayarak sorun olmaz).
mklement0

Eğer boşlukla ilgileniyorsanız tırnak işareti kullanın.
Dave

Lütfen yapın - bu (b) 'yi ele almasa da.
mklement0

Lütfen bunun başarısız olduğu bir b) örneği gösterin. Tanım olarak, bozuk bir sembolik bağlantı, var olmayan bir dizin girişini gösterir. Bu komut dosyasının amacı, sembolik bağlantıları diğer yöne çözümlemektir. Symlink kırılmış olsaydı komut dosyasını yürütmezdiniz. Bu örnek, yürütülmekte olan komut dosyasının çözümlenmesini göstermeyi amaçlamaktadır.
Dave

“şu ​​anda yürütülmekte olan senaryoyu çözmeyi gösterme amaçlı” - aslında, odaklanmayı seçtiğiniz sorunun kapsamının daralması; öyle söylediğiniz sürece, gayet iyi. Siz yapmadığınızdan bu yorumu yazdım. Lütfen yanıtın kapsamından bağımsız olarak bir sorun olan alıntı sorununu giderin.
mklement0

2

Not: Bunun sağlam, taşınabilir, hazır bir çözüm olduğuna inanıyorum, bu nedenle bu nedenle her zaman uzun .

Aşağıda, tamamen çapraz platform olan POSIX uyumlu bir komut dosyası / işlev bulunmaktadır (10.12 (Sierra) itibariyle readlinkhala desteklemeyen macOS üzerinde de çalışır -f) - yalnızca POSIX kabuk dili özelliklerini ve yalnızca POSIX uyumlu yardımcı program çağrılarını kullanır .

Bu bir olan GNU en taşınabilir uygulamareadlink -e (daha sıkı sürümü readlink -f).

Sen edebilirsiniz çalıştırmak komut ilesh veya kaynak işlevi de bash, kshvezsh :

Örneğin, bir komut dosyasının içinde, sembolik bağlantıların çözülmesiyle çalışan komut dosyasının gerçek başlangıç ​​dizinini almak için aşağıdaki gibi kullanabilirsiniz:

trueScriptDir=$(dirname -- "$(rreadlink "$0")")

rreadlink kod / fonksiyon tanımı:

Kod bu cevaptan şükranla uyarlandı .
Ayrıca burada Node.js yüklüyse, yükleyebileceğiniz bashtek tabanlı bir yardımcı program sürümü oluşturdum .
npm install rreadlink -g

#!/bin/sh

# SYNOPSIS
#   rreadlink <fileOrDirPath>
# DESCRIPTION
#   Resolves <fileOrDirPath> to its ultimate target, if it is a symlink, and
#   prints its canonical path. If it is not a symlink, its own canonical path
#   is printed.
#   A broken symlink causes an error that reports the non-existent target.
# LIMITATIONS
#   - Won't work with filenames with embedded newlines or filenames containing 
#     the string ' -> '.
# COMPATIBILITY
#   This is a fully POSIX-compliant implementation of what GNU readlink's
#    -e option does.
# EXAMPLE
#   In a shell script, use the following to get that script's true directory of origin:
#     trueScriptDir=$(dirname -- "$(rreadlink "$0")")
rreadlink() ( # Execute the function in a *subshell* to localize variables and the effect of `cd`.

  target=$1 fname= targetDir= CDPATH=

  # Try to make the execution environment as predictable as possible:
  # All commands below are invoked via `command`, so we must make sure that
  # `command` itself is not redefined as an alias or shell function.
  # (Note that command is too inconsistent across shells, so we don't use it.)
  # `command` is a *builtin* in bash, dash, ksh, zsh, and some platforms do not 
  # even have an external utility version of it (e.g, Ubuntu).
  # `command` bypasses aliases and shell functions and also finds builtins 
  # in bash, dash, and ksh. In zsh, option POSIX_BUILTINS must be turned on for
  # that to happen.
  { \unalias command; \unset -f command; } >/dev/null 2>&1
  [ -n "$ZSH_VERSION" ] && options[POSIX_BUILTINS]=on # make zsh find *builtins* with `command` too.

  while :; do # Resolve potential symlinks until the ultimate target is found.
      [ -L "$target" ] || [ -e "$target" ] || { command printf '%s\n' "ERROR: '$target' does not exist." >&2; return 1; }
      command cd "$(command dirname -- "$target")" # Change to target dir; necessary for correct resolution of target path.
      fname=$(command basename -- "$target") # Extract filename.
      [ "$fname" = '/' ] && fname='' # !! curiously, `basename /` returns '/'
      if [ -L "$fname" ]; then
        # Extract [next] target path, which may be defined
        # *relative* to the symlink's own directory.
        # Note: We parse `ls -l` output to find the symlink target
        #       which is the only POSIX-compliant, albeit somewhat fragile, way.
        target=$(command ls -l "$fname")
        target=${target#* -> }
        continue # Resolve [next] symlink target.
      fi
      break # Ultimate target reached.
  done
  targetDir=$(command pwd -P) # Get canonical dir. path
  # Output the ultimate target's canonical path.
  # Note that we manually resolve paths ending in /. and /.. to make sure we have a normalized path.
  if [ "$fname" = '.' ]; then
    command printf '%s\n' "${targetDir%/}"
  elif  [ "$fname" = '..' ]; then
    # Caveat: something like /var/.. will resolve to /private (assuming /var@ -> /private/var), i.e. the '..' is applied
    # AFTER canonicalization.
    command printf '%s\n' "$(command dirname -- "${targetDir}")"
  else
    command printf '%s\n' "${targetDir%/}/$fname"
  fi
)

rreadlink "$@"

Güvenlik teğetliği:

jarno , yerleşkenincommand aynı ada sahip bir takma ad veya kabuk işlevi tarafından gölgelenmemesini sağlayan işleve istinaden bir yorumda bulunur:

Ya takma adlar veya kabuk işlevleri olarak ayarlanırsa unaliasveya unsetve ne [yapılır?

Orijinal anlamı rreadlinkolmasını sağlamanın ardındaki motivasyon command, onu yeniden tanımlamak gibi etkileşimli kabuklarda standart komutları gölgelemek için sıklıkla kullanılan kullanışlı takma adları ve işlevleri atlamak (benign) kullanmaktır.ls seçenekleri içermek .

Bunun endişesi, güvenilmeyen, kötü niyetli çevre ile o sürece ediyoruz konu işlemi söylemek güvenli olduğunu düşünüyorum unaliasya unsetya, bu konuda, - while, do... - yeniden belirlendiği bir endişe değildir.

Orada bir şey fonksiyonu orijinal anlam ve davranış olması güvenmek gerektiğini - etrafta hiçbir yolu yoktur.
POSIX benzeri mermilerin yerleşiklerin ve hatta dil anahtar kelimelerinin yeniden tanımlanmasına izin vermesi doğal olarak bir güvenlik riskidir (ve genel olarak paranoyak kod yazmak zordur).

Endişelerinizi özellikle ele almak için:

Fonksiyon orijinal anlamlarına dayanır unaliasve unsetonların orijinal anlamlarına sahiptir. Kabuk işlevleri olarak davranışlarını değiştirecek şekilde yeniden tanımlanması bir sorun olacaktır; bir takma ad olarak yeniden tanımlanması bir endişe kaynağı değildir, çünkü komut adının (ör.) alıntılanması (örneğin \unalias) takma adları atlar.

Ancak, alıntı olduğu değil kabuk için bir seçenek anahtar kelimeler ( while, for, if, do, ...) ve kabuk anahtar kabuk üzerinde öncelik taşır yaparken fonksiyonları içinde, bashve zshdiğer adlar böylece çalıştırmalısınız kabuk anahtar kelime yinelenmesi karşı koruma için, en yüksek önceliğe sahip unaliasolan isimleri (her ne kadar etkileşimli olmayan bash Script gibi kabukları () diğer adları olan değil , varsayılan olarak genişletilmiş - yalnızca shopt -s expand_aliasesaçıkça ilk olarak adlandırılır).

unalias- Bir yerleşik olarak - orijinal anlamına sahip olduğundan emin olmak için , önce orijinal anlamına \unsetsahip olmasını gerektiren bunu kullanmalısınız unset:

unsetbir kabuk yerleşiktir , bu yüzden çağrıldığından emin olmak için kendisinin bir işlev olarak yeniden tanımlanmadığından emin olmanız gerekir. . Diğer ad formunu tırnak işareti ile atlayabilirken, kabuk işlevi formunu atlayamazsınız - catch 22.

Bu nedenle, unsetorijinal anlamına güvenemezseniz, anlatabileceğim kadarıyla, tüm kötü amaçlı yeniden tanımlara karşı savunmanın garantili bir yolu yoktur.


Deneyimlerime göre, alıntı, ilk söylediğinizin aksine, kabuk işlevlerini değil, takma adları atlar.
jarno

I tanımlayabilir test edilen [bir takma ad olarak dashve bashve bir kabuk fonksiyonu olarak bash.
jarno

1
Ayrı bir soru olarak bahsetmiştim .
jarno

@jarno: İyi puanlar; Cevabımı güncelledim; hala bir sorun olduğunu düşünüyorsanız bana bildirin.
mklement0

1
@jarno: Sen edebilirsiniz tanımlamak whilebir fonksiyonu olarak bash, kshve zsh(ancak dash), ancak yalnızca function <name>sözdizimi: function while { echo foo; }eserler ( while() { echo foo; }does not). Ancak, bu while anahtar kelimeyi gölgelendirmez , çünkü anahtar kelimeler işlevlerden daha yüksek önceliğe sahiptir (bu işlevi çağırmanın tek yolu şöyledir \while). Gelen bashve zshböylece, takma ad anahtar kelimelere göre daha yüksek önceliğe sahip diğer adı (ancak anahtar kelimeler redefinitions onlara gölge yok bashsadece varsayılan olarak interaktif sürece, kabukları shopt -s expand_aliasesaçıkça denir).
mklement0

1

Genel kabuk komut dosyaları, sembolik bağ olarak çağrılsalar bile genellikle "home" dizinlerini bulmak zorundadırlar. Betiğin bu yüzden "gerçek" pozisyonunu sadece 0 $ 'dan bulması gerekir.

cat `mvn`

sistemimde, aşağıdakileri içeren bir komut dosyası yazdırılır, bu da ihtiyacınız olan şey için iyi bir ipucu olmalıdır.

if [ -z "$M2_HOME" ] ; then
  ## resolve links - $0 may be a link to maven's home
  PRG="$0"

  # need this for relative symlinks
  while [ -h "$PRG" ] ; do
    ls=`ls -ld "$PRG"`
    link=`expr "$ls" : '.*-> \(.*\)$'`
    if expr "$link" : '/.*' > /dev/null; then
      PRG="$link"
    else
      PRG="`dirname "$PRG"`/$link"
    fi
  done

  saveddir=`pwd`

  M2_HOME=`dirname "$PRG"`/..

  # make it fully qualified
  M2_HOME=`cd "$M2_HOME" && pwd`

1

Bunu dene:

cd $(dirname $([ -L $0 ] && readlink -f $0 || echo $0))

1

Yıllar boyunca birçok kez karşılaştığım ve bu sefer OSX ve linux'da kullanabileceğim saf bir bash taşınabilir versiyonuna ihtiyacım olduğundan, devam ettim ve bir tane yazdım:

Yaşayan versiyon burada yaşıyor:

https://github.com/keen99/shell-functions/tree/master/resolve_path

Ancak SO uğruna, şu anki sürüm (iyi test edildiğini hissediyorum ... ama geri bildirime açığım!)

Düz bourne kabuğu (sh) için çalışması zor olmayabilir, ama denemedim ... $ FUNCNAME'i çok seviyorum. :)

#!/bin/bash

resolve_path() {
    #I'm bash only, please!
    # usage:  resolve_path <a file or directory> 
    # follows symlinks and relative paths, returns a full real path
    #
    local owd="$PWD"
    #echo "$FUNCNAME for $1" >&2
    local opath="$1"
    local npath=""
    local obase=$(basename "$opath")
    local odir=$(dirname "$opath")
    if [[ -L "$opath" ]]
    then
    #it's a link.
    #file or directory, we want to cd into it's dir
        cd $odir
    #then extract where the link points.
        npath=$(readlink "$obase")
        #have to -L BEFORE we -f, because -f includes -L :(
        if [[ -L $npath ]]
         then
        #the link points to another symlink, so go follow that.
            resolve_path "$npath"
            #and finish out early, we're done.
            return $?
            #done
        elif [[ -f $npath ]]
        #the link points to a file.
         then
            #get the dir for the new file
            nbase=$(basename $npath)
            npath=$(dirname $npath)
            cd "$npath"
            ndir=$(pwd -P)
            retval=0
            #done
        elif [[ -d $npath ]]
         then
        #the link points to a directory.
            cd "$npath"
            ndir=$(pwd -P)
            retval=0
            #done
        else
            echo "$FUNCNAME: ERROR: unknown condition inside link!!" >&2
            echo "opath [[ $opath ]]" >&2
            echo "npath [[ $npath ]]" >&2
            return 1
        fi
    else
        if ! [[ -e "$opath" ]]
         then
            echo "$FUNCNAME: $opath: No such file or directory" >&2
            return 1
            #and break early
        elif [[ -d "$opath" ]]
         then 
            cd "$opath"
            ndir=$(pwd -P)
            retval=0
            #done
        elif [[ -f "$opath" ]]
         then
            cd $odir
            ndir=$(pwd -P)
            nbase=$(basename "$opath")
            retval=0
            #done
        else
            echo "$FUNCNAME: ERROR: unknown condition outside link!!" >&2
            echo "opath [[ $opath ]]" >&2
            return 1
        fi
    fi
    #now assemble our output
    echo -n "$ndir"
    if [[ "x${nbase:=}" != "x" ]]
     then
        echo "/$nbase"
    else 
        echo
    fi
    #now return to where we were
    cd "$owd"
    return $retval
}

demlemek sayesinde klasik bir örnek:

%% ls -l `which mvn`
lrwxr-xr-x  1 draistrick  502  29 Dec 17 10:50 /usr/local/bin/mvn@ -> ../Cellar/maven/3.2.3/bin/mvn

bu işlevi kullanın ve -real- yolunu döndürecektir:

%% cat test.sh
#!/bin/bash
. resolve_path.inc
echo
echo "relative symlinked path:"
which mvn
echo
echo "and the real path:"
resolve_path `which mvn`


%% test.sh

relative symlinked path:
/usr/local/bin/mvn

and the real path:
/usr/local/Cellar/maven/3.2.3/libexec/bin/mvn 

Benim daha önceki cevabım alanın yaklaşık 1 / 4'ünde aynı şeyi yapıyor :) Bu oldukça basit kabuk komut dosyası ve bir git repo layık değil.
Dave

1

Mac uyumsuzluğunu gidermek için,

echo `php -r "echo realpath('foo');"`

Harika değil, çapraz işletim sistemi


3
Python 2.6+, php'den daha fazla son kullanıcı sisteminde mevcuttur, bu yüzden python -c "from os import path; print(path.realpath('${SYMLINK_PATH}'));"muhtemelen daha anlamlı olacaktır. Yine de, bir kabuk komut dosyasından Python'u kullanmanız gerektiğinde, muhtemelen sadece Python'u kullanmalı ve kendinizi çapraz platform kabuk komut dosyalarının baş ağrısından kurtarmalısınız.
Jonathan Baldwin

Sh built-in readlink, dirname ve basename'den daha fazlasına ihtiyacınız yoktur.
Dave

@Dave: dirname, basenameve readlinkharici olan programları dahili bileşenleri kabuk değildir; dirnameve basenamePOSIX'in bir parçası readlink, değil.
mklement0

@ mklement0 - Çok haklısın. CoreUtils veya eşdeğeri tarafından sağlanır. 01:00 sonra so ziyaret olmamalıdır. Yorumumun özü, ne bir PHP ne de bir temel sistemde yüklü olanın ötesinde başka bir dil tercümanı gerekmemesidir. Bu sayfada verdiğim senaryoyu 1997'den beri her linux varyantında ve 2006'dan beri MacOS X'te hatasız olarak kullandım. OP bir POSIX çözümü istemedi. Spesifik ortamları Mac OS
Dave

Evet,: @Dave olan stok kamu ile yapmak mümkün, ama aynı zamanda bir sabit (senaryonuzun eksiklikleri ile kanıtlandığı gibi) bunu. OS X gerçekten odak olsaydı, o zaman bu cevabı olduğu mükemmel iyi - ve çok daha basit - göz önüne alındığında phpAncak OS X ile birlikte gelen, olsa bile vücut sorununun, bu gibi etiketlenmediyse OS X bahseder ve bu açıkça görmüştür çeşitli platformlardaki insanlar yanıtlar için buraya gelirler, bu nedenle platforma özgü / POSIX olmayanların ne olduğunu belirtmeye değer.
mklement0

1

Bu, Bash'te bağlantının bir dizin mi yoksa dizin dışı mı olduğu ile çalışan bir symlink çözümleyicisidir:

function readlinks {(
  set -o errexit -o nounset
  declare n=0 limit=1024 link="$1"

  # If it's a directory, just skip all this.
  if cd "$link" 2>/dev/null
  then
    pwd -P
    return 0
  fi

  # Resolve until we are out of links (or recurse too deep).
  while [[ -L $link ]] && [[ $n -lt $limit ]]
  do
    cd "$(dirname -- "$link")"
    n=$((n + 1))
    link="$(readlink -- "${link##*/}")"
  done
  cd "$(dirname -- "$link")"

  if [[ $n -ge $limit ]]
  then
    echo "Recursion limit ($limit) exceeded." >&2
    return 2
  fi

  printf '%s/%s\n' "$(pwd -P)" "${link##*/}"
)}

Tüm unutmayın cdve setmalzeme bir kabuktaki gerçekleşir.


Aslında, () {} gibi bir "bileşik komut" olarak sayıldığından () etrafındaki {} gereksizdir. Ancak, işlev adından sonra hala () gerekir.
Chris Cogdon

@ChrisCogdon {}etrafında ()hiçbir yoksa gereksiz değildir ()işlev adını arkasında. Bash, işlev bildirimlerini, ()kabuğun bildirimlerde parametre listeleri içermediği ve çağrılarla yapmaması nedeniyle kabul etmez, ()bu nedenle işlev bildirimleri ()çok anlamlı değildir.
solidsnack

0

Burada şu anda benim için iyi çalışan cevaba bir çapraz platform (en azından Linux ve macOS) çözümü olduğuna inandığım şeyi sunuyorum.

crosspath()
{
    local ref="$1"
    if [ -x "$(which realpath)" ]; then
        path="$(realpath "$ref")"
    else
        path="$(readlink -f "$ref" 2> /dev/null)"
        if [ $? -gt 0 ]; then
            if [ -x "$(which readlink)" ]; then
                if [ ! -z "$(readlink "$ref")" ]; then
                    ref="$(readlink "$ref")"
                fi
            else
                echo "realpath and readlink not available. The following may not be the final path." 1>&2
            fi
            if [ -d "$ref" ]; then
                path="$(cd "$ref"; pwd -P)"
            else
                path="$(cd $(dirname "$ref"); pwd -P)/$(basename "$ref")"
            fi
        fi
    fi
    echo "$path"
}

İşte bir macOS (sadece?) Çözümü. Muhtemelen orijinal soruya daha uygun.

mac_realpath()
{
    local ref="$1"
    if [[ ! -z "$(readlink "$ref")" ]]; then
        ref="$(readlink "$1")"
    fi
    if [[ -d "$ref" ]]; then
        echo "$(cd "$ref"; pwd -P)"
    else
        echo "$(cd $(dirname "$ref"); pwd -P)/$(basename "$ref")"
    fi
}

0

Cevabım Bash: Bir sembolik bağın gerçek yolunu nasıl alabilirim?

ama kısacası senaryolarda çok kullanışlı:

script_home=$( dirname $(realpath "$0") )
echo Original script home: $script_home

Bunlar GNU coreutils'in bir parçasıdır, Linux sistemlerinde kullanıma uygundur.

Her şeyi test etmek için symlink'i / home / test2 / dizinine koyarız, bazı ek şeyleri değiştiririz ve kök dizinden çalıştırır / çağırırız:

/$ /home/test2/symlink
/home/test
Original script home: /home/test

Nerede

Original script is: /home/test/realscript.sh
Called script is: /home/test2/symlink

0

Pwd'nin kullanılamaması durumunda (örn. Farklı bir konumdan komut dosyaları çağırmak), realpath (dirname ile veya dirname olmadan) kullanın:

$(dirname $(realpath $PATH_TO_BE_RESOLVED))

Herhangi bir konumdan (çoklu) symlink'leri çağırırken veya komut dosyasını doğrudan çağırırken çalışır.

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.