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


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. 즉 : 그것은 (의 대상 참조) 심볼릭 링크를 해결하기 위해 당신을 허용하지 않습니다 파일 또는 파손 , 당신은 추가 작업을 할 필요가 기존 디렉토리의 심볼릭 링크를 해결하기위한 심볼릭 링크 (이전 작업 디렉토리를 복원하거나 지역화 cd및 pwd -P통화 서브 쉘에서).
— mklement0

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

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

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 유틸리티와 호환됩니다. 또한 두 파일 사이의 상대 경로를 생성하는 기능도 포함합니다.

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). 에 bash와 zsh있으므로, 별명, 키워드보다 더 높은 우선 순위를 가지고 있습니다 별칭 (그러나 키워드의 재정의가 그들을 그림자 않습니다 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외부에있는 유틸리티 내장 인 쉘하지; dirname및 basenamePOSIX의 일부, 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##*/}"
)}

모든 참고 cd및 set물건 서브 쉘에서 일어난다.


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