OS X의 Bash 스크립트 절대 경로


98

OS X에서 현재 실행중인 스크립트의 절대 경로를 얻으려고합니다.

에 대한 많은 답변을 보았습니다 readlink -f $0. 그러나 OS X readlink는 BSD와 동일하기 때문에 작동하지 않습니다 (GNU 버전에서 작동).

이에 대한 기본 솔루션이 있습니까?





16
$( cd "$(dirname "$0")" ; pwd -P )
Jason S

답변:


88

거기의 realpath()일을 할 것입니다 C의 기능은,하지만 난 명령 줄에서 사용할 수 아무것도 보이지 않아요. 다음은 빠르고 더러운 교체입니다.

#!/bin/bash

realpath() {
    [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}

realpath "$0"

로 시작하는 경우 경로를 그대로 인쇄합니다 /. 그렇지 않은 경우 상대 경로 여야하므로 앞에 추가 $PWD됩니다. #./부분은 오프 스트립 ./의 전면에서 $1.


1
나는 또한 C 함수를 발견했지만 해당 바이너리 또는 아무것도 찾을 수 없습니다. 어쨌든, 당신의 기능은 내 요구에 잘 맞습니다. 감사!
마이티 러버 덕

7
이것은 심볼릭 링크를 역 참조하지 않습니다.
Adam Vandenberg 2011 년

17
realpath ../something반환$PWD/../something
Lri 2011

이것은 나를 위해 일했지만이 bash 스크립트를 리눅스 쉘로 보내야 할 수도 있고 "실제"realpath와 충돌하는 것을 원하지 않았기 때문에 "realpath_osx ()"로 이름을 변경했습니다! (나는 더 우아한 방법이있을거야,하지만 떠들썩한 파티 n00b입니다.)
앤드류 Theken

8
command -v realpath >/dev/null 2>&1 || realpath() { ... }
Kara Brightwell 2013

115

다음 세 가지 간단한 단계를 통해이 문제와 다른 많은 OS X 문제를 해결할 수 있습니다.

  1. Homebrew 설치
  2. brew install coreutils
  3. grealpath .

(3)으로 변경 될 수 있습니다. realpath(2) 출력 참조


3
이렇게하면 문제가 해결되지만 원하지 않거나 필요하지 않거나 대상 시스템에 없을 수있는 항목을 설치해야합니다. 예 : OS X
Jason S

6
@JasonS GNU Coreutils는 사용 하지 않기 에는 너무 좋습니다 . 여기에는 많은 훌륭한 유틸리티가 포함되어 있습니다. 사실 리눅스 커널은 그것 없이는 쓸모없고 왜 어떤 사람들은 그것을 GNU / 리눅스라고 부르는가는 매우 좋습니다. Coreutils는 굉장합니다. 훌륭한 답변이 선택되어야합니다.

7
@omouse이 질문은 '즉각적인 솔루션'(OS X 용)을 구체적으로 언급합니다. coreutils가 얼마나 멋진 지에 관계없이 OS X에서 '기본 제공'이 아니므로 대답은 좋지 않습니다. coreutils가 얼마나 좋은지, OS X에있는 것보다 나은지에 대한 질문이 아닙니다. '즉시 사용할 수있는'솔루션 $( cd "$(dirname "$0")" ; pwd -P )이 저에게 잘 작동합니다.
Jason S

2
OS X의 "기능"중 하나는 디렉토리와 파일 이름이 대소 문자를 구분하지 않는다는 것입니다. 결과적으로 당신은 디렉토리라는있는 경우에 XXX어떤 일이 cd xxx다음 pwd돌아갑니다 .../xxx. 이 답변을 제외하고 위의 모든 솔루션 xxx은 실제로 원하는 것이 XXX. 감사합니다!
앤드류

1
끝없이 코드를 복사하고 붙여넣고 재창조하는 것이 기존 패키지 관리자 솔루션보다 더 나은지 모르겠습니다. 필요한 경우 realpath다른 항목이 필요하면 어떻게됩니까 coreutils? bash에서도 해당 함수를 다시 작성 하시겠습니까? : P
Ezekiel Victor

28

으. 나는 몇 가지 이유로 인해 이전 답변이 약간 필요하다는 것을 발견했습니다. 특히, 그들은 여러 수준의 심볼릭 링크를 해결하지 않으며 매우 "Bash-y"입니다. 원래 질문은 "Bash 스크립트"를 명시 적으로 요구하지만 Mac OS X의 BSD와 유사한 비 GNU에 대해서도 언급 readlink합니다. 그래서 여기에 임의의 수의 심볼릭 링크를 해결하는 합리적인 이식성에 대한 시도가 있습니다 (bash로 'sh'와 dash로 확인했습니다). 유틸리티 자체의 기본 이름에 공백이 있으면 동작을 확신 할 수 없지만 경로의 공백과 함께 작동해야합니다.

#!/bin/sh
realpath() {
  OURPWD=$PWD
  cd "$(dirname "$1")"
  LINK=$(readlink "$(basename "$1")")
  while [ "$LINK" ]; do
    cd "$(dirname "$LINK")"
    LINK=$(readlink "$(basename "$1")")
  done
  REALPATH="$PWD/$(basename "$1")"
  cd "$OURPWD"
  echo "$REALPATH"
}
realpath "$@"

누군가에게 유용 할 수 있기를 바랍니다.


1
local전역 네임 스페이스를 오염시키지 않도록 함수 내에 정의 된 변수 에만 사용 하는 것이 좋습니다 . 예 local OURPWD=.... 적어도 bash에서 작동합니다.
Michael Paesold 2018

또한 코드는 개인 변수에 대문자를 사용하지 않아야합니다. 대문자 변수는 시스템 사용을 위해 예약되어 있습니다.
tripleee 19.01.08

스크립트 주셔서 감사합니다. 링크 (들)과 실제 파일이 다른 기본 이름이 경우는 아마를 추가하는 좋은 아이디어가 될 것입니다 BASENAME=$(basename "$LINK") 동안 및 사용 안에 그 두 번째 LINK 세터와 REALPATH 세터에서
stroborobo

이것은 심볼릭 링크와 ..부모 참조를 처리하지 않습니다 realpath. 사제로 coreutils설치 시도 ln -s /var/log /tmp/linkexamplerealpath /tmp/linkexample/../; 이것은 인쇄합니다 /private/var. 그러나 당신의 함수는 심볼릭 링크가 아니기 /tmp/linkexample/..때문에 대신 생성 합니다 ...
Martijn Pieters

10

명령 줄 친화적 인 Python 솔루션 변형 :

python -c "import os; print(os.path.realpath('$1'))"

2
이런 경우에 사람이 하나의 명령에 대한 파이썬 인터프리터를 시작하는 미친 정도는 ...이다
Bachsau

나는 화가 충분하지만, 사용python -c "import os; import sys; print(os.path.realpath(sys.argv[1]))"
알렉스 체임벌린

7

시스템 프로비저닝 스크립트, 즉 Homebrew가 설치되기 전에 실행되는 솔루션을 찾고있었습니다. 적절한 솔루션이 없으면 Perl과 같은 크로스 플랫폼 언어로 작업을 오프로드합니다.

script_abspath=$(perl -e 'use Cwd "abs_path"; print abs_path(@ARGV[0])' -- "$0")

더 자주 우리가 실제로 원하는 것은 포함하는 디렉토리입니다.

here=$(perl -e 'use File::Basename; use Cwd "abs_path"; print dirname(abs_path(@ARGV[0]));' -- "$0")

큰! Perl은 약간의 오버 헤드에 좋습니다! 첫 번째 버전을 FULLPATH=$(perl -e "use Cwd 'abs_path'; print abs_path('$0')"). 반대 이유가 있습니까?
F Pereira

@FPereira 이스케이프 처리되지 않은 사용자 제공 문자열에서 프로그램 코드를 생성하는 것은 결코 좋은 생각이 아닙니다. ''방탄이 아닙니다. $0예를 들어 작은 따옴표 가 포함되어 있으면 중단됩니다 . 아주 간단한 예 :에서 버전을 시도 하고 전체 경로로 /tmp/'/test.sh호출하십시오 /tmp/'/test.sh.
4ae1e1 2019

또는 더 간단 /tmp/'.sh합니다.
4ae1e1 2019

6

가 있기 때문에 realpath 등의 뾰족한 밖으로있다 :

// realpath.c
#include <stdio.h>
#include <stdlib.h>

int main (int argc, char* argv[])
{
  if (argc > 1) {
    for (int argIter = 1; argIter < argc; ++argIter) {
      char *resolved_path_buffer = NULL;
      char *result = realpath(argv[argIter], resolved_path_buffer);

      puts(result);

      if (result != NULL) {
        free(result);
      }
    }
  }

  return 0;
}

Makefile :

#Makefile
OBJ = realpath.o

%.o: %.c
      $(CC) -c -o $@ $< $(CFLAGS)

realpath: $(OBJ)
      gcc -o $@ $^ $(CFLAGS)

그런 다음 다음으로 컴파일 make하고 소프트 링크를 넣습니다.
ln -s $(pwd)/realpath /usr/local/bin/realpath


그냥 할 수 gcc realpath.c -o /usr/local/bin/realpath있을까요?
Alexander Mills

1
@AlexanderMills 컴파일러를 루트로 실행해서는 안됩니다. 그렇게하지 않으면 다음, 당신은 쓰기에 권한이 없습니다/usr/local/bin
tripleee

6

Python을 사용하여 얻으십시오.

#!/usr/bin/env python
import os
import sys

print(os.path.realpath(sys.argv[1]))

2
abs_path () {    
   echo "$(cd $(dirname "$1");pwd)/$(basename "$1")"
}

dirname의 디렉토리 이름을 줄 것이다 /path/to/file즉, /path/to.

cd /path/to; pwd 경로가 절대적인지 확인합니다.

basename단지 파일 이름을 줄 것이다 /path/to/file즉, file.


이 코드는 질문에 답할 수 있지만이 코드가 질문에 대한 이유 및 / 또는 답변 방법에 대한 추가 컨텍스트를 제공하면 장기적인 가치가 향상됩니다.
Igor F.

1

위에서 볼 수 있듯이 약 6 개월 전에 이것을 촬영했습니다. 나는 비슷한 것을 다시 필요로 할 때까지 그것을 완전히 잊었다. 나는 그것이 얼마나 기초적인지보고 완전히 충격받았다 . 나는 지금까지 약 1 년 동안 꽤 집중적으로 코딩하는 법을 가르쳐 왔지만, 상황이 최악 일 때 전혀 배운 것이없는 것 같은 느낌이 든다.

위의 '솔루션'을 제거하고 싶지만 지난 몇 달 동안 실제로 얼마나 배웠는지에 대한 기록 인 것이 정말 마음에 듭니다.

그러나 나는 탈선한다. 나는 어제 밤에 앉아서 모든 일을했다. 주석의 설명은 충분해야합니다. 내가 계속 작업중인 사본을 추적 하려면이 요점을 따를 수 있습니다. 이것은 아마도 당신이 필요한 것을 할 것입니다.

#!/bin/sh # dash bash ksh # !zsh (issues). G. Nixon, 12/2013. Public domain.

## 'linkread' or 'fullpath' or (you choose) is a little tool to recursively
## dereference symbolic links (ala 'readlink') until the originating file
## is found. This is effectively the same function provided in stdlib.h as
## 'realpath' and on the command line in GNU 'readlink -f'.

## Neither of these tools, however, are particularly accessible on the many
## systems that do not have the GNU implementation of readlink, nor ship
## with a system compiler (not to mention the requisite knowledge of C).

## This script is written with portability and (to the extent possible, speed)
## in mind, hence the use of printf for echo and case statements where they
## can be substituded for test, though I've had to scale back a bit on that.

## It is (to the best of my knowledge) written in standard POSIX shell, and
## has been tested with bash-as-bin-sh, dash, and ksh93. zsh seems to have
## issues with it, though I'm not sure why; so probably best to avoid for now.

## Particularly useful (in fact, the reason I wrote this) is the fact that
## it can be used within a shell script to find the path of the script itself.
## (I am sure the shell knows this already; but most likely for the sake of
## security it is not made readily available. The implementation of "$0"
## specificies that the $0 must be the location of **last** symbolic link in
## a chain, or wherever it resides in the path.) This can be used for some
## ...interesting things, like self-duplicating and self-modifiying scripts.

## Currently supported are three errors: whether the file specified exists
## (ala ENOENT), whether its target exists/is accessible; and the special
## case of when a sybolic link references itself "foo -> foo": a common error
## for beginners, since 'ln' does not produce an error if the order of link
## and target are reversed on the command line. (See POSIX signal ELOOP.)

## It would probably be rather simple to write to use this as a basis for
## a pure shell implementation of the 'symlinks' util included with Linux.

## As an aside, the amount of code below **completely** belies the amount
## effort it took to get this right -- but I guess that's coding for you.

##===-------------------------------------------------------------------===##

for argv; do :; done # Last parameter on command line, for options parsing.

## Error messages. Use functions so that we can sub in when the error occurs.

recurses(){ printf "Self-referential:\n\t$argv ->\n\t$argv\n" ;}
dangling(){ printf "Broken symlink:\n\t$argv ->\n\t"$(readlink "$argv")"\n" ;}
errnoent(){ printf "No such file: "$@"\n" ;} # Borrow a horrible signal name.

# Probably best not to install as 'pathfull', if you can avoid it.

pathfull(){ cd "$(dirname "$@")"; link="$(readlink "$(basename "$@")")"

## 'test and 'ls' report different status for bad symlinks, so we use this.

 if [ ! -e "$@" ]; then if $(ls -d "$@" 2>/dev/null) 2>/dev/null;  then
    errnoent 1>&2; exit 1; elif [ ! -e "$@" -a "$link" = "$@" ];   then
    recurses 1>&2; exit 1; elif [ ! -e "$@" ] && [ ! -z "$link" ]; then
    dangling 1>&2; exit 1; fi
 fi

## Not a link, but there might be one in the path, so 'cd' and 'pwd'.

 if [ -z "$link" ]; then if [ "$(dirname "$@" | cut -c1)" = '/' ]; then
   printf "$@\n"; exit 0; else printf "$(pwd)/$(basename "$@")\n"; fi; exit 0
 fi

## Walk the symlinks back to the origin. Calls itself recursivly as needed.

 while [ "$link" ]; do
   cd "$(dirname "$link")"; newlink="$(readlink "$(basename "$link")")"
   case "$newlink" in
    "$link") dangling 1>&2 && exit 1                                       ;;
         '') printf "$(pwd)/$(basename "$link")\n"; exit 0                 ;;
          *) link="$newlink" && pathfull "$link"                           ;;
   esac
 done
 printf "$(pwd)/$(basename "$newlink")\n"
}

## Demo. Install somewhere deep in the filesystem, then symlink somewhere 
## else, symlink again (maybe with a different name) elsewhere, and link
## back into the directory you started in (or something.) The absolute path
## of the script will always be reported in the usage, along with "$0".

if [ -z "$argv" ]; then scriptname="$(pathfull "$0")"

# Yay ANSI l33t codes! Fancy.
 printf "\n\033[3mfrom/as: \033[4m$0\033[0m\n\n\033[1mUSAGE:\033[0m   "
 printf "\033[4m$scriptname\033[24m [ link | file | dir ]\n\n         "
 printf "Recursive readlink for the authoritative file, symlink after "
 printf "symlink.\n\n\n         \033[4m$scriptname\033[24m\n\n        "
 printf " From within an invocation of a script, locate the script's "
 printf "own file\n         (no matter where it has been linked or "
 printf "from where it is being called).\n\n"

else pathfull "$@"
fi

1
요점 링크가 끊어진 것 같습니다.
Alex

이 대답은 작동합니다 : stackoverflow.com/a/46772980/1223975 .... 그냥 사용해야합니다.
Alexander Mills

구현은 ..상위 참조 전에 심볼릭 링크를 확인하지 않습니다 . 예를 들어 심볼릭 링크가 가리키는 경로의 부모가 아닌 것으로 /foo/link_to_other_directory/..확인됩니다 . 및 해결 각 경로의 구성 요소는 루트에서 시작하여 아직 처리되고 나머지에 붙이는 링크 대상을 업데이트. 그 논리를 다시 구현하는이 질문에 대한 답변을 추가했습니다. /foo/foo/link_to_other_directoryreadlink -frealpath
Martijn Pieters

1

Mac OS X 용 realpath

realpath() {
    path=`eval echo "$1"`
    folder=$(dirname "$path")
    echo $(cd "$folder"; pwd)/$(basename "$path"); 
}

관련 경로가있는 예 :

realpath "../scripts/test.sh"

홈 폴더의 예

realpath "~/Test/../Test/scripts/test.sh"

1
좋은 간단한 솔루션, 방금 호출 할 때 ..올바른 답을 생성하지 않을 때 한 가지주의 사항을 찾았 으므로 주어진 경로가 디렉토리인지 확인을 추가했습니다. if test -d $path ; then echo $(cd "$path"; pwd) ; else [...]
herbert

반면, 나를 위해 작동하지 않았다 "$(dirname $(dirname $(realpath $0)))"... 다른, 그래서 필요 뭔가를 작업을 수행
알렉산더 밀스

쓸모없는 사용echo 도 저질러서는 안됩니다.
tripleee

이것은 실제로 realpath와 같은 방식으로 심볼릭 링크를 해결하지 않습니다. 이후가 아니라 심볼릭 링크 해결 하기 전에.. 부모 참조 해결 합니다 . homebrew가 coreutils설치된 상태에서 시도하고 링크를 ln -s /var/log /tmp/linkexample만든 다음 실행하십시오 realpath /tmp/linkexample/../. 이것은 인쇄합니다 /private/var. 그러나 여전히 cd 뒤에 표시 /tmp/linkexample/..되기 때문에 대신 함수가 생성 됩니다 . pwd/tmp/linkexample
Martijn Pieters

1

macOS에서 symlink를 안정적으로 처리하는 유일한 솔루션은 realpath. 이 작업에는이 필요하므로 brew install coreutils해당 단계를 자동화했습니다. 내 구현은 다음과 같습니다.

#!/usr/bin/env bash

set -e

if ! which realpath >&/dev/null; then
  if ! which brew >&/dev/null; then
    msg="ERROR: This script requires brew. See https://brew.sh for installation instructions."
    echo "$(tput setaf 1)$msg$(tput sgr0)" >&2
    exit 1
  fi
  echo "Installing coreutils/realpath"
  brew install coreutils >&/dev/null
fi

thisDir=$( dirname "`realpath "$0"`" )
echo "This script is run from \"$thisDir\""


이 오류는 brew설치 되지 않은 경우 오류가 발생 하지만 대신 설치할 수도 있습니다. 나는 그물에서 임의의 루비 코드를 컬링하는 것을 자동화하는 것이 편하지 않았다.

이것은 Oleg Mikheev의 답변 에 대한 자동 변형입니다 .


하나의 중요한 테스트

이러한 솔루션에 대한 좋은 테스트는 다음과 같습니다.

  1. 어딘가에 스크립트 파일에 코드를 넣어
  2. 다른 디렉토리에서 ln -s해당 파일에 대한 심볼릭 링크 ( )
  3. 해당 심볼릭 링크 에서 스크립트 실행

솔루션이 심볼릭 링크를 역 참조하고 원래 디렉토리를 제공합니까? 그렇다면 작동합니다.


0

이것은 OSX에서 작동하는 것 같고 바이너리가 필요하지 않으며 여기 에서 가져 왔습니다.

function normpath() {
  # Remove all /./ sequences.
  local path=${1//\/.\//\/}

  # Remove dir/.. sequences.
  while [[ $path =~ ([^/][^/]*/\.\./) ]]; do
    path=${path/${BASH_REMATCH[0]}/}
  done
  echo $path
}

0

나는 이것을 좋아한다 :

#!/usr/bin/env bash
function realpath() {
    local _X="$PWD"
    local _LNK=$1
    cd "$(dirname "$_LNK")"
    if [ -h "$_LNK" ]; then
        _LNK="$(readlink "$_LNK")"
        cd "$(dirname "$_LNK")"
    fi
    echo "$PWD/$(basename "$_LNK")"
    cd "$_X"
}

0

realpathOS X에서 대체물이 필요했습니다. symlink와 부모 참조가 있는readlink -f 경로에서 올바르게 작동하는 입니다. 여기에는 상위 참조 확인 하기 전에 경로의 심볼릭 링크 확인이 포함됩니다 . 예를 들어 홈브류 coreutils병 을 설치 한 경우 다음을 실행합니다.

$ ln -s /var/log/cups /tmp/linkeddir  # symlink to another directory
$ greadlink -f /tmp/linkeddir/..      # canonical path of the link parent
/private/var/log

참고 readlink -f해결하고있다 /tmp/linkeddir 전에 해석 처리 측의 ..부모 디렉토리 참조. 물론 readlink -fMac 에도 없습니다 .

그래서 bash 구현의 일부로 Bash 3.2에서 realpathGNUlib canonicalize_filename_mode(path, CAN_ALL_BUT_LAST)함수 호출이 하는 일을 다시 구현했습니다 . 이것은 또한 GNU readlink -f가 만드는 함수 호출입니다 .

# shellcheck shell=bash
set -euo pipefail

_contains() {
    # return true if first argument is present in the other arguments
    local elem value

    value="$1"
    shift

    for elem in "$@"; do 
        if [[ $elem == "$value" ]]; then
            return 0
        fi
    done
    return 1
}

_canonicalize_filename_mode() {
    # resolve any symlink targets, GNU readlink -f style
    # where every path component except the last should exist and is
    # resolved if it is a symlink. This is essentially a re-implementation
    # of canonicalize_filename_mode(path, CAN_ALL_BUT_LAST).
    # takes the path to canonicalize as first argument

    local path result component seen
    seen=()
    path="$1"
    result="/"
    if [[ $path != /* ]]; then  # add in current working dir if relative
        result="$PWD"
    fi
    while [[ -n $path ]]; do
        component="${path%%/*}"
        case "$component" in
            '') # empty because it started with /
                path="${path:1}" ;;
            .)  # ./ current directory, do nothing
                path="${path:1}" ;;
            ..) # ../ parent directory
                if [[ $result != "/" ]]; then  # not at the root?
                    result="${result%/*}"      # then remove one element from the path
                fi
                path="${path:2}" ;;
            *)
                # add this component to the result, remove from path
                if [[ $result != */ ]]; then
                    result="$result/"
                fi
                result="$result$component"
                path="${path:${#component}}"
                # element must exist, unless this is the final component
                if [[ $path =~ [^/] && ! -e $result ]]; then
                    echo "$1: No such file or directory" >&2
                    return 1
                fi
                # if the result is a link, prefix it to the path, to continue resolving
                if [[ -L $result ]]; then
                    if _contains "$result" "${seen[@]+"${seen[@]}"}"; then
                        # we've seen this link before, abort
                        echo "$1: Too many levels of symbolic links" >&2
                        return 1
                    fi
                    seen+=("$result")
                    path="$(readlink "$result")$path"
                    if [[ $path = /* ]]; then
                        # if the link is absolute, restart the result from /
                        result="/"
                    elif [[ $result != "/" ]]; then
                        # otherwise remove the basename of the link from the result
                        result="${result%/*}"
                    fi
                elif [[ $path =~ [^/] && ! -d $result ]]; then
                    # otherwise all but the last element must be a dir
                    echo "$1: Not a directory" >&2
                    return 1
                fi
                ;;
        esac
    done
    echo "$result"
}

여기에는 순환 심볼릭 링크 감지가 포함되며 동일한 (중간) 경로가 두 번 표시되면 종료됩니다.

필요한 것이 모두 readlink -f이면 위를 다음과 같이 사용할 수 있습니다.

readlink() {
    if [[ $1 != -f ]]; then  # poor-man's option parsing
        # delegate to the standard readlink command
        command readlink "$@"
        return
    fi

    local path result seenerr
    shift
    seenerr=
    for path in "$@"; do
        # by default readlink suppresses error messages
        if ! result=$(_canonicalize_filename_mode "$path" 2>/dev/null); then
            seenerr=1
            continue
        fi
        echo "$result"
    done
    if [[ $seenerr ]]; then
        return 1;
    fi
}

를 들어 realpath, I도 필요 --relative-to하고 --relative-base정규화 후 당신에게 상대 경로를 제공하는 지원 :

_realpath() {
    # GNU realpath replacement for bash 3.2 (OS X)
    # accepts --relative-to= and --relative-base options
    # and produces canonical (relative or absolute) paths for each
    # argument on stdout, errors on stderr, and returns 0 on success
    # and 1 if at least 1 path triggered an error.

    local relative_to relative_base seenerr path

    relative_to=
    relative_base=
    seenerr=

    while [[ $# -gt 0 ]]; do
        case $1 in
            "--relative-to="*)
                relative_to=$(_canonicalize_filename_mode "${1#*=}")
                shift 1;;
            "--relative-base="*)
                relative_base=$(_canonicalize_filename_mode "${1#*=}")
                shift 1;;
            *)
                break;;
        esac
    done

    if [[
        -n $relative_to
        && -n $relative_base
        && ${relative_to#${relative_base}/} == "$relative_to"
    ]]; then
        # relative_to is not a subdir of relative_base -> ignore both
        relative_to=
        relative_base=
    elif [[ -z $relative_to && -n $relative_base ]]; then
        # if relative_to has not been set but relative_base has, then
        # set relative_to from relative_base, simplifies logic later on
        relative_to="$relative_base"
    fi

    for path in "$@"; do
        if ! real=$(_canonicalize_filename_mode "$path"); then
            seenerr=1
            continue
        fi

        # make path relative if so required
        if [[
            -n $relative_to
            && ( # path must not be outside relative_base to be made relative
                -z $relative_base || ${real#${relative_base}/} != "$real"
            )
        ]]; then
            local common_part parentrefs

            common_part="$relative_to"
            parentrefs=
            while [[ ${real#${common_part}/} == "$real" ]]; do
                common_part="$(dirname "$common_part")"
                parentrefs="..${parentrefs:+/$parentrefs}"
            done

            if [[ $common_part != "/" ]]; then
                real="${parentrefs:+${parentrefs}/}${real#${common_part}/}"
            fi
        fi

        echo "$real"
    done
    if [[ $seenerr ]]; then
        return 1
    fi
}

if ! command -v realpath > /dev/null 2>&1; then
    # realpath is not available on OSX unless you install the `coreutils` brew
    realpath() { _realpath "$@"; }
fi

이 코드에 대한 코드 검토 요청에 단위 테스트를 포함했습니다 .


-2

댓글 작성자와의 의사 소통을 기반으로 나는 그것이 매우 어렵고 실제 경로를 구현하는 세 가지 방법이 우분투와 완전히 동일하게 작동한다는 데 동의했습니다.

그러나 다음 버전은 코너 케이스를 처리 할 수있는 베스트 답변으로는 맥북에서 일상적인 요구를 충족시킬 수 없습니다. 이 코드를 ~ / .bashrc에 넣고 기억하십시오.

  • arg는 하나의 파일 또는 dir 만 될 수 있으며 와일드 카드는 사용할 수 없습니다.
  • dir 또는 파일 이름에 공백이 없습니다.
  • 최소한 파일 또는 디렉토리의 상위 디렉토리가 있습니다.
  • 자유롭게 사용하십시오. .. / 물건, 이것들은 안전 해

    # 1. if is a dir, try cd and pwd
    # 2. if is a file, try cd its parent and concat dir+file
    realpath() {
     [ "$1" = "" ] && return 1

     dir=`dirname "$1"`
     file=`basename "$1"`

     last=`pwd`

     [ -d "$dir" ] && cd $dir || return 1
     if [ -d "$file" ];
     then
       # case 1
       cd $file && pwd || return 1
     else
       # case 2
       echo `pwd`/$file | sed 's/\/\//\//g'
     fi

     cd $last
    }

쓸모없는 사용echo 을 피하고 싶습니다 . 그냥 pwd같은 수행 echo $(pwd)쉘의 두 번째 복사본을 산란하지 않고. 또한 인수를 인용하지 않는 echo것은 버그입니다 (앞뒤의 공백, 인접한 내부 공백 문자를 잃고 와일드 카드가 확장되는 등). 추가 참조 stackoverflow.com/questions/10067266/…
tripleee

또한 존재하지 않는 경로에 대한 동작은 버그가 있습니다. 그러나 나는 그것이 아마도 "하지만 기억하라"라는 문장이 말하려는 것 같다. Ubuntu의 동작 realpath은 존재하지 않는 디렉토리 를 요청할 때 현재 디렉토리를 인쇄 하지 않는 것입니다.
tripleee

일관성 dir=$(dirname "$1"); file=$(basename "$1")을 위해 오래 사용되지 않는 백틱 구문 대신 선호 할 수 있습니다. 또한 인수의 올바른 인용도 주목하십시오.
tripleee

업데이트 된 답변은 많은 버그를 수정하지 못하고 새로운 버그를 추가하는 것 같습니다.
tripleee

우분투 18.04 데스크톱에서 수행하는 모든 테스트가 괜찮 기 때문에 특정 실패 사례를 알려주십시오.
occia
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.