Bash에서 가능한 상대 경로 확장


82

내 스크립트에 대한 인수로 일부 파일 경로가 있습니다. 물론 그것들은 상대적 일 수 있습니다 (또는 ~을 포함 할 수 있습니다). 그러나 내가 작성한 함수의 경우 절대 경로가 필요하지만 심볼릭 링크가 해결되지 않았습니다.

이것에 대한 기능이 있습니까?



7
readlink심볼릭 링크를 해결합니다. OP는 그것을 원하지 않습니다.
Keith Thompson



1
echo $(cd some_directory && pwd), symlink를 확인하지 않음, some_directory가 없으면 실패합니다. 작업 디렉토리는 영향을받지 않습니다. 또는 변수에 할당합니다 MY_PATH=$(cd some_directory && pwd).
qeatzy

답변:


79

MY_PATH=$(readlink -f $YOUR_ARG)같은 상대 경로를 해결할 수 "./""../"

이것도 고려하십시오 ( source ) :

#!/bin/bash
dir_resolve()
{
cd "$1" 2>/dev/null || return $?  # cd to desired directory; if fail, quell any error messages but return exit status
echo "`pwd -P`" # output full, link-resolved path
}

# sample usage
if abs_path="`dir_resolve \"$1\"`"
then
echo "$1 resolves to $abs_path"
echo pwd: `pwd` # function forks subshell, so working directory outside function is not affected
else
echo "Could not reach $1"
fi

확실히 좋지만 문제는 simlink가 해결되어 사용자와 약간의 혼란을 초래할 수 있다는 것입니다.
laar

man pwd: 바로 제거 "실제 현재 작업 디렉토리 (모든 심볼릭 링크가 해결) 표시 -P"-P
andsens

7
readlink -fOS X 에서 기능 을 얻으려면 Homebrew 를 사용 하고 다음을 실행할 $ brew install coreutils 수 있습니다. 그런 다음 greadlink -f.
jgpawletko 2015-06-03

45

http://www.linuxquestions.org/questions/programming-9/bash-script-return-full-path-and-filename-680368/page3.html 에는 다음이 있습니다.

function abspath {
    if [[ -d "$1" ]]
    then
        pushd "$1" >/dev/null
        pwd
        popd >/dev/null
    elif [[ -e "$1" ]]
    then
        pushd "$(dirname "$1")" >/dev/null
        echo "$(pwd)/$(basename "$1")"
        popd >/dev/null
    else
        echo "$1" does not exist! >&2
        return 127
    fi
}

pushd/ popd를 사용 하여 pwd유용한 상태 가됩니다.


2
슬프게도 이것은 여기에 대한 최선의 답변이며 그것이 속한 곳으로 투표되지 않았습니다. OP의 질문에 정확하게 답하려면 +1합니다. 그는 심볼릭 링크가 해결되지 않기를 원하는데, 이것은 실제로 bash의 로컬 문제이며 DIRSTACK다른 것은 없습니다. 좋은 해결책!
cptstubing06 2013 년

5
표준을 목표로하는 경우 sh호환성 pushd; cd <dir>; <cmd>; popd으로 대체 할 수있다 (cd <dir>; <cmd>).
Abbafei

16

간단한 한 줄 :

function abs_path {
  (cd "$(dirname '$1')" &>/dev/null && printf "%s/%s" "$PWD" "${1##*/}")
}

용법:

function do_something {
    local file=$(abs_path $1)
    printf "Absolute path to %s: %s\n" "$1" "$file"
}
do_something $HOME/path/to/some\ where

나는 여전히 경로가 존재하는지 여부를 완전히 모호하게 만드는 방법을 찾으려고 노력하고 있습니다 (따라서 파일을 만들 때도 사용할 수 있음).


1
이것은 현재 작업 디렉토리를 변경하지 않습니까? 내가해야 cd -나중에 그것을 재설정?
devios1 2013

4
필요 없음. 괄호를 참고하면 내부 명령이 서브 쉘에서 실행됩니다. 서브 쉘의 상태 (예 : 작업 디렉토리)는 상위 쉘로 누출되지 않습니다. 자세한 내용은 여기를 참조하십시오 : tldp.org/LDP/abs/html/subshells.html
andsens 2011

1
좋은 한 줄짜리. 하지만 문제가 있습니다. $ 1의 확장 된 값에 슬래시가 없으면 (즉, 현재 디렉터리의 파일 이름) $ {1 % / *}가 파일 이름 자체로 확장되고 cd 명령이 실패합니다. 대신 $ (dirname $ 1)을 사용하여 '.'로 확장 할 수 있습니다. 그 경우.
Paulo

당신이 맞아요, 업데이트되었습니다. 내가 대신 일부 인용 문제가 발생 ${1##*/}하여 basename별도의 변수에 넣지 않고, 생각을, 공백 경로가 제대로 작동하지 않습니다.
andsens

1
@BryanP 나는 실제로 내가 유지하고있는 dotfile 동기화기에 대해 완전히 배 밖으로 나가는 것을 끝냈다. 불필요한 경로 부분을 제거하는 clean_path () 함수가 있습니다. 끝에 상대 경로를 붙여서$PWD 실행하면 아주 잘 작동합니다. github.com/andsens/homeshick/blob/…
andsens

8

이것은 OS X에서 나를 위해 트릭을 수행합니다. $(cd SOME_DIRECTORY 2> /dev/null && pwd -P)

어디에서나 작동합니다. 다른 솔루션은 너무 복잡해 보였습니다.


4

OS X에서 사용할 수 있습니다

stat -f "%N" YOUR_PATH

Linux에서는 realpath실행 파일 이있을 수 있습니다 . 그렇지 않은 경우 다음이 작동 할 수 있습니다 (링크뿐 아니라).

readlink -c YOUR_PATH

20
OS X 답변이 작동하지 않습니다. stat -f "%N" PATH내가 준 경로와 똑같은 길을 제공합니다.
Lily Ballard

3

사용 readlink -f <relative-path>예 :

export FULLPATH=`readlink -f ./`

2

아마도 이것은 더 읽기 쉽고 서브 쉘을 사용하지 않으며 현재 디렉토리를 변경하지 않습니다.

dir_resolve() {
  local dir=`dirname "$1"`
  local file=`basename "$1"`
  pushd "$dir" &>/dev/null || return $? # On error, return error code
  echo "`pwd -P`/$file" # output full, link-resolved path with filename
  popd &> /dev/null
}

1

다른 방법이 있습니다. bash 스크립트에 python 임베딩을 사용하여 상대 경로를 확인할 수 있습니다.

abs_path=$(python3 - <<END
from pathlib import Path
path = str(Path("$1").expanduser().resolve())
print(path)
END
)

0

자체 편집, 나는 OP가 해결 된 심볼릭 링크를 찾고 있지 않다고 말했습니다.

"하지만 내가 작성한 함수의 경우 절대 경로가 필요하지만 심볼릭 링크가 해결되지 않은 경로가 필요합니다."

그래서 이것은 결국 그의 질문에 그다지 적절하지 않다고 생각하십시오. :)

수년에 걸쳐이 문제를 여러 번 겪었고 이번에는 OSX와 Linux에서 사용할 수있는 순수한 bash 휴대용 버전이 필요했기 때문에 계속해서 다음을 작성했습니다.

살아있는 버전은 여기에 있습니다.

https://github.com/keen99/shell-functions/tree/master/resolve_path

하지만 그래서, 여기에 현재 버전이 있습니다 (잘 테스트되었다고 느낍니다 ..하지만 피드백에 열려 있습니다!)

평범한 bourne shell (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
}

다음은 brew 덕분에 고전적인 예입니다.

%% 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

0

OS에서 지원하는 경우 다음을 사용하십시오.

realpath "./some/dir"

그리고 변수에서 사용 :

some_path="$(realpath "./some/dir")"

당신의 길을 넓힐 것입니다. Ubuntu 및 CentOS에서 테스트되었으며 귀하의 제품에서 사용하지 못할 수 있습니다. 일부는 readlink를 권장하지만 readlink에 대한 문서는 다음과 같이 말합니다.

realpath (1)은 정규화 기능에 사용하는 기본 명령입니다.

사람들이 내 변수를 인용하는 이유를 궁금해하는 경우 경로에서 공백을 유지하는 것입니다. 하는 것처럼 realpath some path두 가지 경로 결과를 제공합니다. 그러나 realpath "some path"하나를 반환합니다. 인용 된 매개 변수 ftw :)


-1

bash를 독점적으로 사용해야합니까? 저는이 작업을 수행해야했고 Linux와 OS X의 차이점에 지쳤습니다. 그래서 빠르고 더러운 솔루션을 위해 PHP를 사용했습니다.

#!/usr/bin/php <-- or wherever
<?php
{
   if($argc!=2)
      exit();
   $fname=$argv[1];
   if(!file_exists($fname))
      exit();
   echo realpath($fname)."\n";
}
?>

나는 그것이 매우 우아한 해결책이 아니라는 것을 알고 있지만 작동합니다.


이것은 파일 경로를 이미 알고 있거나 파일이 현재 디렉토리에있는 경우 파일 경로를 반환합니다. PATH 설정에서 파일을 찾지 못함
G-Man
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.