シェルスクリプトのシンボリックリンクを解決する方法


220

(Unixライクなシステムでの)絶対パスまたは相対パスが与えられた場合、中間シンボリックリンクを解決した後、ターゲットの完全パスを特定したいと思います。〜username表記も同時に解決することのボーナスポイント。

ターゲットがディレクトリの場合、ディレクトリにchdir()してからgetcwd()を呼び出すことは可能ですが、Cヘルパーを作成するのではなく、シェルスクリプトからこれを実行したいのです。残念ながら、シェルはユーザーからシンボリックリンクの存在を隠そうとする傾向があります(これはOS Xのbashです):

$ 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
$

上記の例のtmpディレクトリから実行するとresolve( "foo")== "/ Users / greg / tmp / bar"になるようなresolve()関数が必要です。

回答:


92

標準に従って、pwd -Pシンボリックリンクが解決されたパスを返す必要があります。

C関数char *getcwd(char *buf, size_t size)from unistd.hは同じ振る舞いを持つべきです。

getcwd pwd


22
これは(現在の)ディレクトリで機能しますか?ターゲットがファイルの場合、何も出力されません...
dala

4
現在のパスにすることができないため、リンクが壊れている場合は機能しません
Tom Howard

6
後知恵の利点で要約すると、この回答は非常に限られた状況でのみ機能します。つまり、対象のシンボリックリンクが実際に存在するディレクトリに対するものである場合です。さらに、をcd呼び出す前に、まずそれを実行する必要がありますpwd -P。言い換えると、ファイルへのシンボリックリンクまたは壊れたシンボリックリンクを解決(ターゲットを参照)できず、既存のディレクトリのシンボリックリンクを解決するには、追加の作業(以前の作業ディレクトリの復元またはローカライズcdpwd -P呼び出し)を行う必要があります。サブシェルで)。
mklement0

悪いことに、ディレクトリではなくファイルを解決する方法を探しています。
Martin

他の人が指摘したように、これは実際には質問に答えません。以下の@pixelbeatの答えはそうです。
erstaples

401
readlink -f "$path"

編集者注:上記はGNU readlinkおよびFreeBSD / PC-BSD / OpenBSD readlinkで動作しますが、10.11以降のOS Xでは動作しません
GNU readlink-m、最終的なターゲットが存在するかどうかにかかわらず、シンボリックリンクを解決するためなど、追加の関連オプションを提供します。

GNU coreutils 8.15(2012-01-06)以降では、上記よりも鈍くなく、より柔軟なrealpathプログラムが利用可能です。同じ名前のFreeBSD utilとも互換性があります。2つのファイル間の相対パスを生成する機能も含まれています。

realpath $path

[ halloleoによるコメントからの管理者の追加— danorton]

Mac OS X(少なくとも10.11.x)ではreadlink-fオプションなしで使用します。

readlink $path

編集者注:これはシンボリックリンクを再帰的に解決しないため、最終的なターゲットを報告しません。たとえば、aを指すシンボリックリンクが与えられb、それが次にを指すc場合、これはレポートするだけですb絶対パスとして出力されることは保証されません)。OS Xで
次のperlコマンドを使用して、不足しているreadlink -f機能のギャップを埋めます。
perl -MCwd -le 'print Cwd::abs_path(shift)' "$path"


5
これは、Mac OS Xで動作しません-参照stackoverflow.com/questions/1055671/...
Bkkbrad

1
OS Xの非互換性についての恥、そうでなければいい+1
jkp

11
OS Xでは、homebrewでcoreutilsをインストールできます。「grealpath」としてインストールします。
キエフ

10
readlinkOSX上で動作しますが、別の構文を必要とします:readlink $path なし-f
Halloleo 2013年

2
readlinkはシンボリックリンクの複数のレイヤーの逆参照に失敗しますが、一度に1つのレイヤーを逆参照するだけです
Magnus

26

「pwd -P」は、ディレクトリが必要な場合に機能するようですが、何らかの理由で実際の実行可能ファイルの名前が必要な場合は、役に立たないと思います。これが私の解決策です:

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

私のお気に入りの1つは realpath foo

realpath-正規化された絶対パス名を返す

realpathは、すべてのシンボリックリンクを展開し、パスと
       正規化された絶対パス名を、resolved_pa​​thで指定されたサイズPATH_MAXのバッファーに格納します。結果のパスにはシンボリックリンクがありません、 '/。/'または
       '/../'コンポーネント。

Debian(etch以降)では、このコマンドはrealpathパッケージに含まれています。
Phil Ross、

2
realpathは現在(2012年1月)coreutilsの一部であり、debianおよびBSDバリアントと下位互換性があります
pixelbeat

1
realpathCentOS 6にはGNU coreutils 8.4.31 がありません。UnixとLinuxで、GNU coreutilsがパッケージ化されていない パッケージをいくつか見つけましたrealpath。したがって、バージョンだけでなく依存しているようです。
toxalot 2014年

私が好むrealpath以上readlink、それは以下のようなオプションフラグを提供していますので、--relative-to
arr_sea

10
readlink -e [filepath]

まさにあなたが求めているもののようです-それは任意のパスを受け入れ、すべてのシンボリックリンクを解決し、「実際の」パスを返します-そしてそれはおそらくすべてのシステムがすでに持っている「標準* nix」です


ちょうどそれに噛まれました。Macでは動作しません。代わりのものを探しています。
dhill

5

別の方法:

# 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が必要です。これは再帰関数であるため、リンクが見つかるまで各ディレクトリに移動します。リンクが見つかった場合、realpathを返し、sedはパスを置き換えます。
Keymon、

5

readlinkがほとんどのシステムで利用可能であるが異なる引数が必要であることを知って、与えられたソリューションのいくつかをまとめると、これはOSXとDebianで私にとってうまく機能します。BSDシステムについてはわかりません。OSXからのみ[[ $OSTYPE != darwin* ]]除外する必要があるかもしれませ-fん。

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

3

インラインPerlスクリプトを使用して、MacOS / Unixでファイルへの実際のパスを取得する方法は次のとおりです。

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

同様に、symlinkedファイルのディレクトリを取得するには:

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

3

パスはディレクトリですか、それともファイルですか?ディレクトリの場合、それは簡単です:

(cd "$DIR"; pwd -P)

ただし、ファイルの場合、これは機能しません。

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

シンボリックリンクが相対パスまたは完全パスに解決される可能性があるためです。

スクリプトでは、実際のパスを見つける必要があるため、一緒にインストールされている構成またはその他のスクリプトを参照できるように、これを使用します。

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

SOURCE任意のファイルパスに設定できます。基本的に、パスがシンボリックリンクである限り、そのシンボリックリンクを解決します。トリックはループの最後の行にあります。解決されたシンボリックリンクが絶対である場合、それはとして使用しますSOURCE。ただし、それが相対である場合は、DIRfor を付加します。これは、最初に説明した簡単なトリックによって実際の場所に解決されました。


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"))/..

これは、(a)空白またはシェルのメタ文字を含むパス、および(b)壊れたシンボリックリンク((a)が適用されないと仮定して、実行中のスクリプトの親パスが必要な場合の問題ではない)で壊れます。
mklement0

空白が気になる場合は引用符を使用してください。
Dave、

してください-それは(b)には対処しません。
mklement0

これが失敗するb)の例を見せてください。定義上、壊れたシンボリックリンクは、存在しないディレクトリエントリを指します。このスクリプトの目的は、反対方向のシンボリックリンクを解決することです。シンボリックリンクが壊れていると、スクリプトを実行できなくなります。この例は、現在実行中のスクリプトを解決する方法を示すことを目的としています。
Dave、

「現在実行中のスクリプトの解決を示すことを目的としています」-確かに、これは、あなたが焦点を当てることを選択した質問の範囲を狭めることです。あなたがそう言う限り、それは完全に素晴らしいです。あなたがそうしなかったので、私はコメントでそれを述べました。回答の範囲に関係なく問題となる引用問題を修正してください。
mklement0

2

注:これは、確実で移植可能な既製のソリューションであると私は信じています。そのため、常に長い時間がかかります。

以下は完全にPOSIX準拠のスクリプト/関数であり、したがってクロスプラットフォームです(macOS readlinkでも機能-fし、10.12(Sierra)の時点ではまだサポートされていません)-POSIXシェル言語機能とPOSIX準拠のユーティリティ呼び出しのみを使用します。

これは、GNUの移植可能な実装ですreadlink -e(より厳密なバージョンのreadlink -f)。

あなたはできる実行スクリプトをshたりソース機能をbashkshzsh

たとえば、スクリプト内で次のように使用して、実行中のスクリプトの真の元のディレクトリを取得し、シンボリックリンクを解決できます。

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

rreadlink スクリプト/関数定義:

コードは、この回答からの感謝の気持ちで適応されました。Node.jsがインストールされている場合は、でインストールできるスタンドアロンベースのユーティリティバージョンもここ
作成しました。bash
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 "$@"

セキュリティの正接:

jarnoは、組み込みcommandが同じ名前のエイリアスまたはシェル関数によってシャドウされないことを保証する関数に関して、コメントで尋ねます:

unaliasまたはunset[エイリアスまたはシェル関数として設定されている場合はどうなりますか?

本来の意味rreadlinkcommand持つようにすることの背後にある動機は、お気に入りのオプションを含めるように再定義するなど、対話型シェルで標準コマンドをシャドウするためによく使用される便利なエイリアスと関数をバイパスするために使用することlsです。

私はそれを気にせ、信頼できない、悪質な環境とそのあなたない限りしている取引を言っても安全だと思うunaliasか、unsetあるいは、そのことについては、 - whiledo、... -再定義されて心配されていません。

あり、何かの機能がその本来の意味と動作を持つことに頼らなければならないことは-その周りに方法はありません。
POSIXのようなシェルは組み込みの再定義を可能にし、言語のキーワードでさえ本質的にセキュリティリスクです(そして、偏執的なコードを書くことは一般に困難です)。

特に懸念事項に対処するには:

関数は、元の意味に依存しunalias、それunsetを持っています。それらの振る舞いを変えるような方法でそれらをシェル関数として再定義させるのは問題でしょう。コマンド名(など)を引用するとエイリアスがバイパスされるため、エイリアスとしての再定義は必ずしも問題ではありません。\unalias

しかし、引用はありませんシェルのオプションキーワードwhileforifdo、...)とシェルキーワードは、シェルよりも優先行いながら機能に、bashそしてzshあなたが実行する必要がありますシェル・キーワードの再定義を防ぐために、別名最高の優先順位を持っているunaliasとそれらの名前(非対話型 bashシェル(スクリプトなど)では)エイリアスはデフォルトで展開されません - shopt -s expand_aliasesが最初に明示的に呼び出された場合のみ)。

unalias-組み込みとして-が元の意味を持つことを確認するには、最初\unsetにそれを使用する必要があります。これにはunset、元の意味が必要です。

unset組み込みシェルなので、そのように呼び出されるようにするには、それ自体が関数として再定義されていないことを確認する必要があります。引用符でエイリアスフォームをバイパスすることはできますが、シェル関数フォーム-キャッチ22はバイパスできません。

したがって、unset元の意味を信頼できる場合を除いて、私が言えることから、悪意のある再定義をすべて防ぐ方法は保証されていません。


私の経験では、最初に言ったように、引用はシェル関数ではなくエイリアスをバイパスします。
jarno 2016年

私は私が定義できることをテスト[で別名としてdashbashし、シェル関数としてbash
jarno 2016年

1
私は別の質問として私の主張をしました
jarno 2016年

@jarno:良い点。回答を更新しました。まだ問題があると思われる場合はお知らせください。
mklement0 2016年

1
@jarno:あなたはできる定義while関数としてbashkshおよびzsh(ではないdash)が、唯一とfunction <name>構文:function while { echo foo; }作品(while() { echo foo; }しません)。しかし、この習慣は、」シャドウwhile キーワードをキーワードが(この関数を呼び出すための唯一の方法としてある機能よりも高い優先順位を持っているので、\while)。でbashzshなるよう、エイリアスは、キーワードよりも高い優先順位を持つエイリアス(ただし、内のキーワードの再定義が彼らを影でくださいbashだけではデフォルトで対話的な場合を除き、シェルshopt -s expand_aliases明示的に呼び出されます)。
mklement0 2016年

1

一般的なシェルスクリプトは、シンボリックリンクとして呼び出された場合でも、多くの場合、「ホーム」ディレクトリを見つける必要があります。したがって、スクリプトは$ 0から「実際の」位置を見つける必要があります。

cat `mvn`

私のシステムでは、以下を含むスクリプトを出力します。これは、必要なものの良いヒントになるはずです。

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

これを試して:

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

1

私は何年にもわたってこれに何度も遭遇し、今回はOSXとLinuxで使用できる純粋なbashポータブルバージョンが必要だったので、先に進んで1つ作成しました。

生きているバージョンはここにあります:

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

しかし、SOのために、ここに現在のバージョンがあります(十分にテストされているように感じますが、フィードバックは受け付けています!)

プレーンボーンシェル(sh)で動作させることは難しくないかもしれませんが、私は試みませんでした... $ FUNCNAMEが好きすぎます。:)

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

ここにbrewのおかげで古典的な例があります:

%% 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

この関数を使用すると、-real-パスが返されます。

%% 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 

私の以前の答えは、スペースの約1/4でまったく同じことを行います:)これはかなり基本的なシェルスクリプトであり、gitリポジトリに値するものではありません。
Dave

1

Macの非互換性を回避するために、私は思いつきました

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

素晴らしいわけではありませんが、クロスOS


3
Python 2.6+は、phpよりも多くのエンドユーザーシステムで利用できるため、python -c "from os import path; print(path.realpath('${SYMLINK_PATH}'));"おそらくもっと理にかなっています。それでも、シェルスクリプトからPythonを使用する必要があるときには、おそらくPythonを使用して、クロスプラットフォームシェルスクリプトの頭痛を解消する必要があります。
ジョナサンボールドウィン

sh組み込みのreadlink、dirname、basename以外は必要ありません。
Dave

@Dave: 、dirnamebasenameおよびreadlink外部にあるユーティリティビルトインをシェルではありません、。dirnameおよびbasenamePOSIXの一部である、readlinkではありません。
mklement0

@ mklement0-あなたはまったく正しい。これらはCoreUtilsまたは同等のものによって提供されます。私は午前1時以降にSOに行くべきではありません。私のコメントの本質は、基本システムにインストールされている以上のPHPやその他の言語インタープリターは必要ないということです。このページで提供したスクリプトを使用しました 1997年以降のすべての Linuxバリアントと2006年以降のMacOS Xでエラーなく使用しました。OPはPOSIXソリューションを要求しませんでした。彼らの特定の環境では、Mac OS Xのだった
デイブ

@デイブ:はい、それ株式ユーティリティでそれを行うこと可能ですが、それはまたです困難です(スクリプトの欠点から明らかです)。OS Xが本当にフォーカスされていた場合、OS Xに付属していることを考えると、この答え完全にうまく、はるかに単純ですphp。ただし、質問の本文でOS Xについて言及している場合でも、そのようにタグ付けされておらず、明らかになりますさまざまなプラットフォームの人々が答えを求めてここに来るので、プラットフォーム固有/非POSIXとは何かを指摘する価値があります。
mklement0 2015年

1

これはBashのシンボリックリンクリゾルバーであり、リンクがディレクトリでも非ディレクトリでも機能します。

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

すべてのことに注意cdしてsetものがサブシェルで行われます。


()は{}と同様に「複合コマンド」としてカウントされるため、実際には()の周りの{}は不要です。ただし、関数名の後に()が必要です。
クリスコグドン2017年

@ChrisCogdon 関数名の後ろに何もなければ{}周囲は()不要ではありません()()シェルは宣言にパラメーターリストを持たず、呼び出し()()行わないため、Bashは関数宣言を受け入れません。
solidsnack

0

ここでは、現在私にとってうまく機能している答えに対するクロスプラットフォーム(少なくともLinuxおよびmacOS)ソリューションであると私が信じるものを提示します。

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

これはmacOS(のみ?)ソリューションです。おそらく元の質問により適しています。

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

私の答えはここバッシュ:シンボリックリンクの実際のパスを取得する方法?

要するに、スクリプトでは非常に便利です。

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

これらはGNU coreutilsの一部であり、Linuxシステムでの使用に適しています。

すべてをテストするために、シンボリックリンクを/ home / test2 /に入れ、いくつかの追加事項を修正して、ルートディレクトリから実行/呼び出しします。

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

どこ

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

0

pwdを使用できない場合(たとえば、別の場所からスクリプトを呼び出す場合)は、(dirnameの有無にかかわらず)realpathを使用します。

$(dirname $(realpath $PATH_TO_BE_RESOLVED))

(複数の)シンボリックリンクを介して呼び出す場合、またはスクリプトを直接呼び出す場合の両方で機能します-任意の場所から。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.