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/linkexample후 realpath /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.