쉘 스크립트에서 심볼릭 링크를 해결하는 방법


220

절대 또는 상대 경로 (유닉스 계열 시스템에서)가 주어지면 중간 심볼릭 링크를 해결 한 후 대상의 전체 경로를 결정하고 싶습니다. ~ 사용자 이름 표기법을 동시에 해결하기위한 보너스 포인트.

대상이 디렉토리 인 경우 디렉토리에 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)에서이 unistd.h같은 동작을한다.

getcwd 암호


22
이것은 (현재) 디렉토리에서만 작동합니까? 대상이 파일이면 아무 것도주지 않습니다.
dala

4
현재 경로를 만들 수 없어 링크가 끊어지면 작동하지 않습니다.
Tom Howard

6
후시의 이점을 요약하면 다음과 같습니다.이 답변은 매우 제한된 상황에서만 작동합니다. 즉, 관심있는 심볼릭 링크 가 실제로 존재 하는 디렉토리 에 대한 경우 ; 또한 cd전화하기 전에 먼저 확인 해야합니다 pwd -P. 즉 : 그것은 (의 대상 참조) 심볼릭 링크를 해결하기 위해 당신을 허용하지 않습니다 파일 또는 파손 , 당신은 추가 작업을 할 필요가 기존 디렉토리의 심볼릭 링크를 해결하기위한 심볼릭 링크 (이전 작업 디렉토리를 복원하거나 지역화 cdpwd -P통화 서브 쉘에서).
mklement0

나쁜 것은 디렉토리가 아니라 파일을 해결하는 방법을 찾는 것입니다.
Martin

다른 사람들이 지적했듯이 이것은 실제로 질문에 대답하지 않습니다. @pixelbeat의 답변은 다음과 같습니다.
erstaples 2019

401
readlink -f "$path"

편집자 주 : 위의 내용은 GNU readlinkFreeBSD / PC-BSD / OpenBSD readlink 에서는 작동하지만 10.11부터 OS X 에서는 작동 하지 않습니다 .
GNU readlink-m최종 대상이 존재하는지 여부에 관계없이 심볼릭 링크를 해결하기위한 추가 관련 옵션을 제공 합니다.

GNU coreutils 8.15 (2012-01-06) 이후로 위의 것보다 덜 애매하고 융통성 이있는 realpath 프로그램이 있습니다. 또한 같은 이름의 FreeBSD 유틸리티와 호환됩니다. 또한 두 파일 사이의 상대 경로를 생성하는 기능도 포함합니다.

realpath $path

[에 의해 코멘트에서 아래의 관리 또한 halloleo - danorton]

Mac OS X (최소 10.11.x 이상)의 readlink경우 -f옵션 없이 사용 하십시오 .

readlink $path

편집자 주 : 이것은 심볼릭 링크를 재귀 적으로 해결 하지 않으므로 궁극적 인 목표를 보고하지 않습니다 . 예를 들어, a을 가리키는 symlink가을 b가리키는 경우 c, 이것은 단지보고 만하고 절대 경로b 로 출력되도록 보장하지는 않습니다 . 누락 된 기능 의 차이를 채우려면 OS X 에서 다음 명령을 사용하십시오 .
perlreadlink -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"로 설치합니다.
Kief

10
readlinkOSX에서 작동하지만 다른 구문이 필요합니다 : readlink $path 없이-f .
halloleo

2
readlink는 심볼릭 링크의 여러 계층을 역 참조하지 못하지만 한 번에 하나의 계층 만 참조합니다
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

내가 가장 좋아하는 것 중 하나는 realpath foo

realpath-정규화 된 절대 경로 이름을 반환

realpath는 모든 기호 링크를 확장하고 path로 명명 된 널 종료 문자열에서 '/./', '/../'및 추가 '/'문자에 대한 참조를 분석합니다.
       정규화 된 절대 경로 이름을 resolved_path로 명명 된 PATH_MAX 크기의 버퍼에 저장합니다. 결과 경로에는 기호 링크 '/./'또는
       '/../'구성 요소.

데비안 (에칭 이후)에서는이 명령을 realpath 패키지에서 사용할 수 있습니다.
Phil Ross

2
realpath 지금 (2012 월)로 coreutils의 부분과 데비안과 BSD 변형과 역 호환
pixelbeat

1
나는이없는 realpathGNU는 8.4.31을로 coreutils로를 CentOS 6. 유닉스 및 리눅스 에서 GNU 코어 유틸리티를 패키지화 하지 않은 다른 여러 가지를 보았습니다 realpath. 따라서 그것은 단순한 버전 이상의 것에 의존하는 것 같습니다.
toxalot

내가 선호하는 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가 필요합니다. 재귀 함수이기 때문에 링크를 찾을 때까지 각 디렉토리로 이동합니다. 링크를 찾으면 실제 경로를 반환 한 다음 sed가 경로를 대체합니다.
Keymon

5

주어진 솔루션 중 일부를 종합하여 readlink가 대부분의 시스템에서 사용 가능하지만 다른 인수가 필요하다는 것을 알고 OSX 및 데비안에서 잘 작동합니다. 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')")

마찬가지로 심볼릭 링크 된 파일의 디렉토리를 가져 오려면 다음을 수행하십시오.

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. 그러나 상대적인 경우 앞에 붙일 DIR것입니다. 이는 앞서 설명한 간단한 트릭으로 실제 위치로 해결되었습니다.


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하지만 여전히 -f10.12 (Sierra)를 지원하지 않음 ). POSIX 쉘 언어 기능 만 사용하고 POSIX 호환 유틸리티 호출 만 사용합니다. .

그것은이다 GNU의 휴대용 구현readlink -e (의 엄격한 버전 readlink -f).

당신은 할 수 있습니다 실행 스크립트 와를sh 또는 소스 기능bash, ksh그리고zsh :

예를 들어, 스크립트 내에서 다음과 같이 스크립트를 사용하여 심볼릭 링크가 해결 된 실행중인 스크립트의 실제 디렉토리를 얻을 수 있습니다.

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[별칭 또는 쉘 함수로 설정?

원래 의미를 rreadlink갖도록 하는 동기 command는 재정의와 같은 대화식 쉘에서 표준 명령을 음영 처리하는 데 사용되는 편의 별칭 및 기능 을 우회 하는 데 사용하는 것입니다.ls 는 선호하는 옵션을 포함하도록 하는 것입니다.

나는 그것이 걱정, 신뢰할 수없는, 악의적 인 환경과 당신 않는있는 거 거래 말을하는 것이 안전하다고 생각 unalias하거나 unset, 또는 그 문제에 대해, - while, do... - 재정의되는 문제가되지 않습니다.

함수가 원래의 의미와 동작을하기에 의존해야하는가 - 그 주위에 방법이 없습니다.
POSIX와 같은 쉘은 내장의 재정의를 허용하며 언어 키워드조차도 본질적 으로 보안 위험이 있습니다 (그리고 편집증 코드를 작성하는 것은 일반적으로 어렵습니다).

특별히 우려 사항을 해결하려면 :

이 함수에 의존 unalias하고 unset원래의 의미를 가지고. 동작을 변경하는 방식으로 쉘 함수 로 재정의 하면 문제가됩니다. 명령 이름 (예 :)을 인용 (일부 )하여 별칭을 우회 하기 때문에 별칭으로 재정의하는 것이 반드시 걱정되는 것은 아닙니다 .\unalias

그러나 인용은 쉘의 옵션 이 아닙니다 . 키워드 ( while, for, if, do, ...)와 쉘 키워드는 쉘을 통해보다 우선 할 때 기능 에, bash그리고 zsh별명은 그래서 당신은 실행해야합니다 쉘 키워드 재정의 방지하려면, 가장 높은 우선 순위를 가지고 unalias와 이름 (에 있지만, 대화 형이 아닌 bash 같은 스크립트와 같은 쉘 () 별명이되어 있지 기본적으로 확장 - 경우에만 shopt -s expand_aliases명시 적 처음이라고합니다).

되도록하려면 unalias- 내장 명령으로 - 원래의 의미를 가지고, 당신은 사용해야 \unset즉 요구하는, 처음에 unset: 원래의 의미가

unset내장 쉘 이므로, 그렇게 호출되도록하려면, 그 자체가 함수 로 재정의되지 않아야 합니다 . 인용 부호를 사용하여 별명 양식을 생략 할 수 있지만 쉘 기능 양식-catch 22를 생략 할 수 없습니다.

따라서 unset내가 말할 수있는 것에서 원래 의미를 가질 수 없다면 모든 악의적 인 재정의에 대해 방어 할 수있는 보장 된 방법이 없습니다.


내 경험상 인용은 처음 말한 것과 달리 셸 함수가 아닌 별칭을 무시합니다.
jarno

[별칭으로 dash그리고 bash에 쉘 함수로 정의 할 수 있는지 테스트 했습니다 bash.
jarno

1
나는 별도의 질문 으로 내 요지를 했다 .
jarno

@jarno : 좋은 점; 내 답변을 업데이트했습니다. 여전히 문제가 있다고 생각되면 알려주십시오.
mklement0

1
@jarno : 당신은 할 수 있습니다 정의 while의 함수로 bash, ksh그리고 zsh(그러나 dash) 만에 function <name>구문 : function while { echo foo; }작품 ( while() { echo foo; }하지 않습니다). 그러나,이 습관은 '그림자 while 키워드를 키워드 기능보다 우선 순위 (이 기능은 같다 호출 할 수있는 유일한 방법을 가지고 있기 때문에, \while). 에 bashzsh있으므로, 별명, 키워드보다 더 높은 우선 순위를 가지고 있습니다 별칭 (그러나 키워드의 재정의가 그들을 그림자 않습니다 bash만 기본적으로 상호 작용 하지 않는 한, 조개 shopt -s expand_aliases명시 적으로 호출한다).
mklement0

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 휴대용 버전이 필요했기 때문에 다음과 같이 작성했습니다.

살아있는 버전은 여기에 산다 :

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
}

양조 덕분에 고전적인 예가 있습니다.

%% 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 repo에 적합하지 않습니다.
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}'));"아마도 더 합리적입니다. 그럼에도 불구하고 쉘 스크립트에서 파이썬을 사용해야 할 때까지는 아마도 파이썬을 사용하고 크로스 플랫폼 쉘 스크립팅의 어려움을 덜어 야 할 것입니다.
Jonathan Baldwin

sh 내장 readlink, dirname 및 basename 이상이 필요하지 않습니다.
Dave

@ 데이브 : dirname, basename, 및 readlink외부에있는 유틸리티 내장 인 쉘하지; dirnamebasenamePOSIX의 일부, readlink아니다.
mklement0

@ mklement0-당신 말이 맞아요. CoreUtils 또는 이와 동등한 기능으로 제공됩니다. 오전 1시 이후에는 방문하지 않아야합니다. 내 의견의 핵심은 PHP 나 기본 시스템에 설치된 언어 이외의 다른 언어 인터프리터가 필요하지 않다는 것입니다. 1997 년 이후 모든 리눅스 변종과 2006 년 이후 MacOS X 에서이 페이지에서 제공 한 스크립트 를 오류없이 사용했습니다. OP는 POSIX 솔루션을 요구하지 않았습니다. 그들의 특정 환경은 Mac OS X였습니다.
Dave

네, : @ 데이브 입니다 재고 유틸리티와 함께 할 수 있지만, 또한 하드 (스크립트의 단점에 의해 입증) 그렇게 할 수 있습니다. OS X 진정이 분명했습니다 경우,이 대답은 입니다 완벽하게 정상적으로 - 그리고 훨씬 더 간단 - 그 주어진 php그러나 OS X를 함께 제공은 비록 질문은,이 같은 태그가되지 않는 OS X 언급, 그것은 분명되고있다 다양한 플랫폼의 사람들이 여기에 답변을 제공하기 때문에 플랫폼 별 / 비 POSIX를 지적 할 가치가 있습니다.
mklement0

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

모든 참고 cdset물건 서브 쉘에서 일어난다.


실제로 ()는 {}와 마찬가지로 "복합 명령"으로 계산되므로 () 주변의 {}은 필요하지 않습니다. 그러나 여전히 함수 이름 뒤에 ()가 필요합니다.
Chris Cogdon

@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

내 대답은 여기 Bash : symlink의 실제 경로를 얻는 방법?

그러나 간단히 말해서 스크립트에서 매우 편리합니다.

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

이들은 Linux 시스템에서 사용하기에 적합한 GNU coreutils의 일부입니다.

모든 것을 테스트하기 위해 symlink를 / 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))

(여러) symlink를 통해 호출 할 때 또는 어느 위치에서나 직접 스크립트를 호출 할 때 작동합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.