Bash에서 파일 경로를 어떻게 정규화합니까?


203

나는 변환 할 /foo/bar/../foo

이것을 수행하는 bash 명령이 있습니까?


편집 : 실제 경우에는 디렉토리가 존재합니다.


4
경우 중요합니까 /foo/bar심지어는 나 /foo실제로 존재, 또는 당신은 단지 경로 이름 규칙에 따라 문자열 조작 측면에 관심이 있습니까?
Chen Levy

5
@twalberg ... 그건 좀 생각이 났어요 ...
Camilo Martin

2
이 문제는 요청 정확히 않습니다 - - 변환 @CamiloMartin 전혀 고안되지 않음 /foo/bar/..에를 /foo하고 사용하여 bash명령을. 언급되지 않은 다른 요구 사항이있는 경우, 아마도 다음과 같아야합니다.
twalberg

14
@twalberg 당신은 너무 많은 TDD를하고 있습니다 -_- '
Camilo Martin

답변:


192

경로에서 파일 이름의 일부를 잘라내려면 "dirname"과 "basename"이 친구이며 "realpath"도 편리합니다.

dirname /foo/bar/baz 
# /foo/bar 
basename /foo/bar/baz
# baz
dirname $( dirname  /foo/bar/baz  ) 
# /foo 
realpath ../foo
# ../foo: No such file or directory
realpath /tmp/../tmp/../tmp
# /tmp

realpath 대안

realpath쉘에서 지원하지 않는 경우 시도해 볼 수 있습니다

readlink -f /path/here/.. 

또한

readlink -m /path/there/../../ 

와 동일하게 작동

realpath -s /path/here/../../

경로를 정규화하기 위해 존재할 필요가 없다는 점에서


5
OS X 솔루션이 필요한 사용자는 아래에서 Adam Liss의 답변을 확인하십시오.
Trenton

stackoverflow.com/a/17744637/999943 이것은 강력한 답변입니다! 나는 하루에이 두 QA 게시물을 발견했고 서로 연결하고 싶었습니다.
phyatt

realpath는 2012 년에 coreutils에 추가 된 것으로 보입니다 . github.com/coreutils/coreutils/commits/master/src/realpath.c 의 파일 히스토리를 참조하십시오 .
노아 라빈

1
모두 realpathreadlinkGNU 기본 유틸리티에서 온다, 그래서 대부분의 아마 당신도 모두 또는 아무것도 얻을 수 없습니다. 내가 올바르게 기억한다면 Mac의 readlink 버전은 GNU 버전과 약간 다릅니다 :-\
Antoine 'hashar'Musso

100

이 작업을 수행하는 직접 bash 명령이 있는지 모르겠지만 일반적으로 수행합니다.

normalDir="`cd "${dirToNormalize}";pwd`"
echo "${normalDir}"

잘 작동합니다.


9
소프트 링크는 정규화되지만 해결되지는 않습니다. 이것은 버그이거나 기능 일 수 있습니다. :-)
Adam Liss

4
$ CDPATH가 정의 된 경우에도 문제가 있습니다. "cd foo"는 현재 디렉토리에있는 "foo"뿐만 아니라 $ CDPATH의 하위 디렉토리 인 "foo"디렉토리로 전환되기 때문입니다. CDPATH = ""cd "$ {dirToNormalize}"&& pwd -P와 같은 작업을 수행해야한다고 생각합니다.
mjs

7
Tim의 대답은 확실히 가장 단순하고 이식성이 뛰어납니다. CDPATH는 다루기 쉽다 : dir = "$ (unset CDPATH && cd"$ dir "&& pwd)"
David Blevins

2
rm -rf $normalDirdirToNormalize가 없으면 매우 위험 할 수 있습니다 ( )!
Frank Meulenaar

4
예, &&@DavidBlevins의 의견에 따라 를 사용하는 것이 가장 좋습니다 .
Elias Dorneles

54

시도하십시오 realpath. 아래는 공개 소스에 기증 된 전체 소스입니다.

// realpath.c: display the absolute path to a file or directory.
// Adam Liss, August, 2007
// This program is provided "as-is" to the public domain, without express or
// implied warranty, for any non-profit use, provided this notice is maintained.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libgen.h>   
#include <limits.h>

static char *s_pMyName;
void usage(void);

int main(int argc, char *argv[])
{
    char
        sPath[PATH_MAX];


    s_pMyName = strdup(basename(argv[0]));

    if (argc < 2)
        usage();

    printf("%s\n", realpath(argv[1], sPath));
    return 0;
}    

void usage(void)
{
    fprintf(stderr, "usage: %s PATH\n", s_pMyName);
    exit(1);
}

1
이것은 표준 bash 설치의 일부로 포함됩니까? 시스템에서 "명령을 찾을 수 없습니다"(RHEL의 bash 3.00.15 (1))
Tim Whitcomb

4
gnu.org/software/coreutils readlink를위한,하지만 realpath이 패키지에서 제공 : packages.debian.org/unstable/utils/realpath
켄트 프레드릭에게

8
그것은 / BSD,하지를 CentOS / OSX 우분투에 기본입니다
에릭 Aronesty

4
"Bonus : bash 명령이며 모든 곳에서 사용 가능합니다." 에 대한 부분 realpath. 또한 내가 가장 좋아하는 것이지만 소스에서 도구를 작성하는 대신. 그것은 모두 헤비급이며, 대부분의 시간은 readlink -f... BTW readlink는 bash 내장이 아니라 coreutils우분투의 일부입니다 .
Tomasz Gandor

4
귀하는 이것을 공개 도메인에 기부한다고 말했지만 헤더에도 "비영리 용도"라고 표시되어 있습니다. 공개 도메인에 기부해야합니까? 그것은 그것이 영리 목적뿐만 아니라 영리 목적으로도 사용될 수 있다는 것을 의미합니다…
me_and

36

이식성이 뛰어나고 안정적인 솔루션은 파이썬을 사용하는 것입니다. 파이썬은 다윈을 포함하여 거의 모든 곳에 사전 설치되어 있습니다. 두 가지 옵션이 있습니다.

  1. abspath 절대 경로를 반환하지만 심볼릭 링크를 확인하지 않습니다.

    python -c "import os,sys; print(os.path.abspath(sys.argv[1]))" path/to/file

  2. realpath 절대 경로를 반환하면 심볼릭 링크가 해결되어 표준 경로가 생성됩니다.

    python -c "import os,sys; print(os.path.realpath(sys.argv[1]))" path/to/file

각각의 경우 path/to/file상대 또는 절대 경로 일 수 있습니다.


7
고마워, 그게 효과가 있었던 유일한 사람이었습니다. OS X에서는 readlink 또는 realpath를 사용할 수 없습니다. Python은 대부분의 플랫폼에 있어야합니다.
sorin

1
명확히하기 위해 옵션이 readlink아닌 OS X에서 사용할 수 -f있습니다. 휴대용 해결 방법은 여기에서 설명 합니다 .
StvnW

3
링크를 따르고 싶지 않다면 이것이 유일한 해결책이라는 말은 문자 그대로입니다. 유닉스가 내 발을 내딛었습니다.
DannoHung

34

coreutils 패키지의 readlink 유틸리티를 사용하십시오.

MY_PATH=$(readlink -f "$0")

1
BSD에는 -f 플래그가 없으므로 최신 MacOS Mojave 및 기타 여러 시스템에서도 실패합니다. 이식성을 원한다면 -f를 사용하는 것을 잊어라. 많은 OS가 영향을 받는다.
sorin

1
@ 소린. 문제는 Mac에 관한 것이 아니라 Linux에 관한 것입니다.
mattalxndr

14

readlink절대 경로를 얻기위한 bash 표준입니다. 또한 경로 또는 경로가 존재하지 않는 경우 빈 플래그를 반환하는 이점이 있습니다 (플래그가 제공됨).

존재하거나 존재하지 않을 수 있지만 부모가있는 디렉토리에 대한 절대 경로를 얻으려면 다음을 사용하십시오.

abspath=$(readlink -f $path)

모든 부모와 함께 존재해야하는 디렉토리의 절대 경로를 얻으려면 다음을 수행하십시오.

abspath=$(readlink -e $path)

주어진 경로를 정식화하고 존재하는 경우 심볼릭 링크를 따르지만 누락 된 디렉토리를 무시하고 경로를 반환하려면 다음과 같습니다.

abspath=$(readlink -m $path)

유일한 단점은 readlink가 링크를 따른다는 것입니다. 링크를 따르지 않으려면 다음 대체 규칙을 사용할 수 있습니다.

abspath=$(cd ${path%/*} && echo $PWD/${path##*/})

$ path의 디렉토리 부분으로 chdir하고 $ path의 파일 부분과 함께 현재 디렉토리를 인쇄합니다. chdir에 실패하면 빈 문자열과 stderr에 오류가 발생합니다.


7
readlink가능한 경우 좋은 옵션입니다. OS X 버전은 -e또는 -f옵션을 지원하지 않습니다 . 처음 세 가지 예에서는 $path파일 이름에서 공백이나 와일드 카드를 처리하기 위해 큰 따옴표를 묶어야합니다 . 매개 변수 확장의 경우 +1이지만 보안 취약점이 있습니다. path비어 있으면 cd홈 디렉토리로 이동합니다. 큰 따옴표가 필요합니다. abspath=$(cd "${path%/*}" && echo "$PWD/${path##*/}")
toxalot

이것은 단지 예일뿐입니다. 보안에 구애받지 않는다면 bash 또는 다른 쉘 변형을 전혀 사용하지 않아야합니다. 또한 bash는 플랫폼 간 호환성과 주요 버전 간의 기능 변경 관련 문제가있을 때 자체적 인 문제가 있습니다. OSX는 쉘 스크립팅과 관련된 문제를 가진 많은 플랫폼 중 하나 일 뿐이며 BSD를 기반으로한다는 것은 말할 것도 없습니다. 진정한 멀티 플랫폼이어야 할 경우 POSIX를 준수해야하므로 매개 변수 확장은 실제로 불가능합니다. 언젠가 Solaris 또는 HP-UX를 살펴보십시오.
Craig

6
여기에 어떤 위반도 의미하지 않지만, 이와 같은 모호한 문제를 지적하는 것이 중요합니다. 나는이 사소한 문제에 대한 빠른 대답을 원하고 있으며 위의 주석이 아닌 경우 모든 입력으로 해당 코드를 신뢰했을 것입니다. 이 배쉬 토론에서 OS-X를 지원하는 것도 중요합니다. 불행히도 OS-X에서 지원되지 않는 많은 명령이 있으며, 많은 포럼은 Bash를 논의 할 때 당연한 것으로 받아들입니다.
Rebs

13

오래된 질문이지만 쉘 수준에서 전체 경로 이름처리 하는 경우 훨씬 간단한 방법 이 있습니다 .

   abspath = "$ (cd"$ path "&& pwd)"

CD는 서브 쉘에서 발생하므로 기본 스크립트에 영향을 미치지 않습니다.

쉘 내장 명령이 -L 및 -P를 허용한다고 가정하면 다음과 같은 두 가지 변형이 있습니다.

   abspath = "$ (cd -P"$ path "&& pwd -P)" "# 심볼릭 링크가 해결 된 물리적 경로
   abspath = "$ (cd -L"$ path "&& pwd -L)" "# 논리적 경로 보존 심볼릭 링크

개인적으로, 어떤 이유로 든 상징적 인 링크에 매료되지 않으면 나중에이 접근법이 거의 필요하지 않습니다.

참고 : 스크립트가 현재 디렉토리를 나중에 변경하더라도 작동하는 스크립트의 시작 디렉토리를 얻는 변형.

name0 = "$ (기본 이름"$ 0 ")"; # 기본 스크립트 이름
dir0 = "$ (cd"$ (dirname "$ 0") ""&& pwd) "; # 절대 시작 디렉토리

CD를 사용하면 스크립트가 ./script.sh와 같은 명령으로 실행 되더라도 cd / pwd없이 종종 ..를 제공하는 경우에도 항상 절대 디렉토리를 갖게됩니다. 스크립트가 나중에 CD를 수행하는 경우에는 쓸모가 없습니다.


8

Adam Liss가 언급했듯이 realpath모든 배포판에 번들로 제공되는 것은 아닙니다. 이것이 가장 좋은 해결책이기 때문에 부끄러운 일입니다. 제공된 소스 코드가 훌륭하므로 지금 사용하기 시작할 것입니다. 여기까지 내가 지금까지 사용해 왔던 내용이 있습니다.

get_abs_path() {
     local PARENT_DIR=$(dirname "$1")
     cd "$PARENT_DIR"
     local ABS_PATH="$(pwd)"/"$(basename "$1")"
     cd - >/dev/null
     echo "$ABS_PATH"
} 

당신이 해결의 심볼릭 링크를 원하는 경우에, 다만 교체 pwd와 함께 pwd -P.


pwd -P이 경우에 대한 옵션을 가진 하나의 문제는 ... $(basename "$1")다른 디렉토리에있는 파일에 대한 심볼릭 링크 인 경우 어떻게 될지 고려하십시오 . 는 pwd -P단지 기본 이름 부분을 경로의 디렉토리 부분에 심볼릭 링크를 해결 있지만.
toxalot

7

내 최근 해결책은 다음과 같습니다.

pushd foo/bar/..
dir=`pwd`
popd

Tim Whitcomb의 답변을 바탕으로합니다.


인수가 디렉토리가 아닌 경우 이것이 실패한다고 생각합니다. / usr / bin / java가 어디로 연결되는지 알고 싶습니다.
Edward Falk

1
파일이라는 것을 알고 있다면 pushd $(dirname /usr/bin/java)시도해 볼 수 있습니다.
schmunk

5

정확히 대답은 아니지만 후속 질문 일 수도 있습니다 (원래 질문은 명시 적이 지 않음).

readlink실제로 심볼릭 링크를 따르고 싶다면 괜찮습니다. 그러나 단순히 정상화에 대한 사용 사례도있다 ./..///순수 구문 적으로 수행 할 수 있습니다 시퀀스 없이 심볼릭 링크를 정규화는. readlink이것에 좋지 않으며 둘 다 아닙니다 realpath.

for f in $paths; do (cd $f; pwd); done

기존 경로에서는 작동하지만 다른 경로에서는 작동하지 않습니다.

sed(스크립트는 반복적으로 시퀀스를 대체 할 수 있다는 점을 제외하고, 좋은 베팅이 될 것 같다 /foo/bar/baz/../..-> /foo/bar/..> - /foo모든 시스템에 가정, 또는 어떤 추한 루프를 사용하여 출력 비교하는 것은 안전하지 않습니다 펄, 같은 것을 사용하지 않고) sed로를 입력.

Java (JDK 6+)를 사용하는 단일 라이너 FWIW :

jrunscript -e 'for (var i = 0; i < arguments.length; i++) {println(new java.io.File(new java.io.File(arguments[i]).toURI().normalize()))}' $paths

realpath-s심볼릭 링크와 만 해결 참조를 해결하지에 옵션 /./, /../여분의 분리 /문자. 이 -m옵션 과 결합 realpath하면 파일 이름에서만 작동하며 실제 파일은 건드리지 않습니다. 그것은 소리 완벽한 솔루션처럼. 그러나 아아, realpath여전히 많은 시스템에서 누락되었습니다.
toxalot

..심볼릭 링크가 포함 된 경우 구성 요소를 구문 적으로 제거 할 수 없습니다. /one/two/../three로 동일하지 않은 /one/three경우 two에 심볼릭 링크입니다 /foo/bar.
jrw32982는 Monica

@ jrw32982 네 대답에서 말했듯이 이것은 symlink 정규화가 필요 하지 않거나 사용되지 않는 유스 케이스 입니다.
Jesse Glick

@JesseGlick 그것은 당신이 심볼릭 링크를 정규화 할 것인지 아닌지의 경우가 아닙니다. 알고리즘이 실제로 잘못된 답을 생성합니다. 당신의 대답이 정확하기 위해서는 관련된 심볼릭 링크가 없었거나 특정 형태의 것임을 미리 알고 있어야합니다. 귀하의 답변에 따르면 경로에 관련된 심볼릭 링크가 아니라 정식화하고 싶지 않습니다.
jrw32982는

고정 된 기존 디렉토리 구조를 가정하지 않고 정규화를 수행해야하는 사용 사례가 있습니다. URI 정규화는 비슷합니다. 이러한 경우 나중에 결과가 적용되는 디렉토리 근처에 심볼릭 링크가있는 경우 결과가 일반적으로 정확하지 않다는 고유 한계가 있습니다.
Jesse Glick

5

나는 파티에 늦었지만 다음과 같이 많은 스레드를 읽은 후에 만들어진 솔루션입니다.

resolve_dir() {
        (builtin cd `dirname "${1/#~/$HOME}"`'/'`basename "${1/#~/$HOME}"` 2>/dev/null; if [ $? -eq 0 ]; then pwd; fi)
}

이것은 $ 1의 절대 경로를 해결하고 ~로 잘 플레이하고 경로에 symlinks를 유지하며 디렉토리 스택을 망칠 수 없습니다. 전체 경로를 반환하거나 존재하지 않는 경우 아무것도 반환하지 않습니다. $ 1은 디렉토리가 될 것으로 예상하고 그렇지 않으면 실패 할 것입니다.하지만 쉽게 확인할 수 있습니다.


4

말이 많고 약간 늦었습니다. 오래된 RHEL4 / 5에 붙어 있기 때문에 하나를 작성해야합니다. 절대 및 상대 링크를 처리하고 //, /./ 및 somedir /../ 항목을 단순화합니다.

test -x /usr/bin/readlink || readlink () {
        echo $(/bin/ls -l $1 | /bin/cut -d'>' -f 2)
    }


test -x /usr/bin/realpath || realpath () {
    local PATH=/bin:/usr/bin
    local inputpath=$1
    local changemade=1
    while [ $changemade -ne 0 ]
    do
        changemade=0
        local realpath=""
        local token=
        for token in ${inputpath//\// }
        do 
            case $token in
            ""|".") # noop
                ;;
            "..") # up one directory
                changemade=1
                realpath=$(dirname $realpath)
                ;;
            *)
                if [ -h $realpath/$token ] 
                then
                    changemade=1
                    target=`readlink $realpath/$token`
                    if [ "${target:0:1}" = '/' ]
                    then
                        realpath=$target
                    else
                        realpath="$realpath/$target"
                    fi
                else
                    realpath="$realpath/$token"
                fi
                ;;
            esac
        done
        inputpath=$realpath
    done
    echo $realpath
}

mkdir -p /tmp/bar
(cd /tmp ; ln -s /tmp/bar foo; ln -s ../.././usr /tmp/bar/link2usr)
echo `realpath /tmp/foo`

3

GitHub에 배치 한 무료 Bash 라이브러리 제품인 realpath-lib 를 무료로 사용해보십시오 . 철저하게 문서화되어 있으며 훌륭한 학습 도구를 만듭니다.

로컬, 상대 및 절대 경로를 해결하고 Bash 4+를 제외한 종속성이 없습니다. 어디에서나 작동합니다. 무료이며 깨끗하고 단순하며 유익합니다.

넌 할 수있어:

get_realpath <absolute|relative|symlink|local file path>

이 기능은 라이브러리의 핵심입니다.

function get_realpath() {

if [[ -f "$1" ]]
then 
    # file *must* exist
    if cd "$(echo "${1%/*}")" &>/dev/null
    then 
        # file *may* not be local
        # exception is ./file.ext
        # try 'cd .; cd -;' *works!*
        local tmppwd="$PWD"
        cd - &>/dev/null
    else 
        # file *must* be local
        local tmppwd="$PWD"
    fi
else 
    # file *cannot* exist
    return 1 # failure
fi

# reassemble realpath
echo "$tmppwd"/"${1##*/}"
return 0 # success

}

또한 get_dirname, get_filename, get_ stemname 및 validate_path에 대한 함수도 포함합니다. 여러 플랫폼에서 사용해보고 개선에 도움이됩니다.


2

문제 realpath는 BSD (또는 그 문제에 대한 OSX)에서 사용할 수 없다는 것입니다. 다음은 Linux Journal에서 다소 오래된 (2009) 기사에서 추출한 간단한 레시피입니다 .

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

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

이 변형 에는 경로가 필요 하지 않습니다 .


그러나 이것은 심볼릭 링크를 해결하지 못합니다. Realpath는 루트에서 시작하는 경로를 처리하고 진행되는 동안 심볼릭 링크를 따릅니다. 이 모든 것은 부모 참조 축소입니다.
Martijn Pieters

2

@Andre의 답변을 바탕으로 누군가가 루프가없는 완전 문자열 조작 기반 솔루션을 따르는 경우 약간 더 나은 버전이있을 수 있습니다. 또한 사용의 단점 어떤 심볼릭 링크, 역 참조하지 않으려는 사람들을 위해 유용 realpath또는 readlink -f.

bash 버전 3.2.25 이상에서 작동합니다.

shopt -s extglob

normalise_path() {
    local path="$1"
    # get rid of /../ example: /one/../two to /two
    path="${path//\/*([!\/])\/\.\./}"
    # get rid of /./ and //* example: /one/.///two to /one/two
    path="${path//@(\/\.\/|\/+(\/))//}"
    # remove the last '/.'
    echo "${path%%/.}"
}

$ normalise_path /home/codemedic/../codemedic////.config
/home/codemedic/.config

이것은 좋은 생각이지만, 다양한 버전의 bash에서 작동하도록 20 분을 낭비했습니다. 이것이 작동하려면 extglob 쉘 옵션이 켜져 있어야하며 기본적으로는 아닙니다. bash 기능과 관련하여 필요한 버전과 기본이 아닌 옵션을 모두 지정하는 것이 중요합니다. 세부 사항은 OS마다 다를 수 있기 때문입니다. 예를 들어 최신 버전의 Mac OSX (Yosemite)에는 오래된 버전의 bash (3.2) 만 제공됩니다.
drwatsoncode

죄송합니다. @ricovox; 나는 지금 그것들을 업데이트했다. 나는 당신이 가지고있는 Bash의 정확한 버전을 알고 싶어합니다. 위의 공식 (업데이트 됨)은 CentOS 5.8에서 작동하며 bash 3.2.25
bashοδεMεδιϲ

혼란을 드려 죄송합니다. 이 코드 DID는 extglob을 켜면 내 Mac OSX bash 버전 (3.2.57)에서 작동합니다. bash 버전에 대한 나의 메모는보다 일반적인 것입니다 (bash의 정규 표현식에 관한 다른 답변에 실제로 더 적용됩니다).
drwatsoncode

2
그래도 대답 해 주셔서 감사합니다. 나는 그것을 나의 기초로 사용했다. 우연히 나는 당신이 실패하는 경우를 발견했습니다 : (1) 상대 경로 hello/../world(2) 파일 이름의 점 /hello/..world(3) 이중 슬래시 후 점 /hello//../world(4) 이중 슬래시 전후 점 /hello//./world또는 /hello/.//world (5) 현재 후 부모 : /hello/./../world/ (6) 부모 부모 후 : /hello/../../world, 등 - 일부 이들의 경로가 변경 끝까지 수정을 위해 루프를 사용하여 고정 될 수있다. (또한 제거 dir/../하지 /dir/..만, 삭제 dir/..끝에서.)
drwatsoncode

0

loveborg의 훌륭한 파이썬 스 니펫을 기반으로 다음과 같이 썼습니다.

#!/bin/sh

# Version of readlink that follows links to the end; good for Mac OS X

for file in "$@"; do
  while [ -h "$file" ]; do
    l=`readlink $file`
    case "$l" in
      /*) file="$l";;
      *) file=`dirname "$file"`/"$l"
    esac
  done
  #echo $file
  python -c "import os,sys; print os.path.abspath(sys.argv[1])" "$file"
done

0
FILEPATH="file.txt"
echo $(realpath $(dirname $FILEPATH))/$(basename $FILEPATH)

파일이 존재하지 않더라도 작동합니다. 파일이 들어있는 디렉토리가 있어야합니다.


GNU Realpath는 다음을 사용하지 않는 한 경로의 마지막 요소가 없어도됩니다.realpath -e
Martijn Pieters

0

세 가지를 모두 수행하는 솔루션이 필요했습니다.

  • 스톡 Mac에서 작업하십시오. realpath그리고 readlink -f애드온입니다
  • 심볼릭 링크 해결
  • 오류 처리

답은 # 1과 # 2를 모두 가지고 있지 않습니다. 다른 야크 면도를 줄이기 위해 # 3을 추가했습니다.

#!/bin/bash

P="${1?Specify a file path}"

[ -e "$P" ] || { echo "File does not exist: $P"; exit 1; }

while [ -h "$P" ] ; do
    ls="$(ls -ld "$P")"
    link="$(expr "$ls" : '.*-> \(.*\)$')"
    expr "$link" : '/.*' > /dev/null &&
        P="$link" ||
        P="$(dirname "$P")/$link"
done
echo "$(cd "$(dirname "$P")"; pwd)/$(basename "$P")"

인용문을 완전히 연습하기 위해 경로에 약간의 공백이있는 짧은 테스트 사례가 있습니다.

mkdir -p "/tmp/test/ first path "
mkdir -p "/tmp/test/ second path "
echo "hello" > "/tmp/test/ first path / red .txt "
ln -s "/tmp/test/ first path / red .txt " "/tmp/test/ second path / green .txt "

cd  "/tmp/test/ second path "
fullpath " green .txt "
cat " green .txt "

0

나는 이것이 고대의 질문이라는 것을 안다. 나는 여전히 대안을 제공하고 있습니다. 최근에 나는 같은 문제를 만났으며 기존의 휴대용 명령을 찾지 못했습니다. 그래서 나는 트릭을 수행 할 수있는 기능을 포함하는 다음 쉘 스크립트를 작성했습니다.

#! /bin/sh                                                                                                                                                

function normalize {
  local rc=0
  local ret

  if [ $# -gt 0 ] ; then
    # invalid
    if [ "x`echo $1 | grep -E '^/\.\.'`" != "x" ] ; then
      echo $1
      return -1
    fi

    # convert to absolute path
    if [ "x`echo $1 | grep -E '^\/'`" == "x" ] ; then
      normalize "`pwd`/$1"
      return $?
    fi

    ret=`echo $1 | sed 's;/\.\($\|/\);/;g' | sed 's;/[^/]*[^/.]\+[^/]*/\.\.\($\|/\);/;g'`
  else
    read line
    normalize "$line"
    return $?
  fi

  if [ "x`echo $ret | grep -E '/\.\.?(/|$)'`" != "x" ] ; then
    ret=`normalize "$ret"`
    rc=$?
  fi

  echo "$ret"
  return $rc
}

https://gist.github.com/bestofsong/8830bdf3e5eb9461d27313c3c282868c


0

나는 가능한 최고의 성능 (재미있게)에 초점을 맞춰 이것을 처리하기 위해 내장 전용 기능을 만들었습니다. 심볼릭 링크를 해결하지 않으므로 기본적으로와 동일합니다 realpath -sm.

## A bash-only mimic of `realpath -sm`. 
## Give it path[s] as argument[s] and it will convert them to clean absolute paths
abspath () { 
  ${*+false} && { >&2 echo $FUNCNAME: missing operand; return 1; };
  local c s p IFS='/';  ## path chunk, absolute path, input path, IFS for splitting paths into chunks
  local -i r=0;         ## return value

  for p in "$@"; do
    case "$p" in        ## Check for leading backslashes, identify relative/absolute path
    '') ((r|=1)); continue;;
    //[!/]*)  >&2 echo "paths =~ ^//[^/]* are impl-defined; not my problem"; ((r|=2)); continue;;
    /*) ;;
    *)  p="$PWD/$p";;   ## Prepend the current directory to form an absolute path
    esac

    s='';
    for c in $p; do     ## Let IFS split the path at '/'s
      case $c in        ### NOTE: IFS is '/'; so no quotes needed here
      ''|.) ;;          ## Skip duplicate '/'s and '/./'s
      ..) s="${s%/*}";; ## Trim the previous addition to the absolute path string
      *)  s+=/$c;;      ### NOTE: No quotes here intentionally. They make no difference, it seems
      esac;
    done;

    echo "${s:-/}";     ## If xpg_echo is set, use `echo -E` or `printf $'%s\n'` instead
  done
  return $r;
}

참고 : 경로 시작시 //정확히 두 개의 이중 슬래시가 구현 정의 동작이므로이 함수는로 시작하는 경로를 처리하지 않습니다 . 그러나, 처리 /, ///등 잘합니다.

이 함수는 모든 경우를 올바르게 처리하는 것처럼 보이지만 아직 처리하지 않은 부분이있을 수 있습니다.

성능 참고 : 수천 개의 인수와 함께 호출 될 abspath때보 다 약 10 배 느리게 실행됩니다 realpath -sm. 단일 인수로 호출하면 매번 새 프로그램을 실행할 필요가 없기 때문에 내 컴퓨터 abspath보다 110 배 이상 빠르게 realpath -sm실행됩니다.


-1

오늘 stat명령을 사용하여 경로 를 확인할 수 있음을 발견했습니다 .

"~ / Documents"와 같은 디렉토리의 경우 :

당신은 이것을 실행할 수 있습니다 :

stat -f %N ~/Documents

전체 경로를 얻으려면

/Users/me/Documents

심볼릭 링크의 경우 % Y 형식 옵션을 사용할 수 있습니다.

stat -f %Y example_symlink

다음과 같은 결과가 반환 될 수 있습니다.

/usr/local/sbin/example_symlink

다른 버전의 * NIX에서는 형식화 옵션이 다를 수 있지만 OSX에서는 나에게 적합했습니다.


1
stat -f %N ~/Documents라인은 쉘이 대체 ... 붉은 청어 ~/Documents/Users/me/Documents, 그리고 stat단지 그대로 인수를 인쇄합니다.
danwyand

-4

다음을 사용하는 간단한 솔루션 node.js:

#!/usr/bin/env node
process.stdout.write(require('path').resolve(process.argv[2]));
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.