스크립트의 절대 경로를 얻는 휴대용 방법?


29

(zsh) 스크립트가 절대 경로를 결정하는 이식 가능한 방법은 무엇입니까?

리눅스에서는 다음과 같은 것을 사용합니다.

mypath=$(readlink -f $0)

...하지만 휴대용이 아닙니다. (예를 들어, readlink다윈에서는 -f깃발을 인식하지 못하며 이에 상응하는 것도 없습니다.) (또한 readlink이것을 사용 하는 것은 꽤 모호한 모양의 핵입니다.)

더 편리한 방법은 무엇입니까?


답변:


28

를 사용하면 다음과 zsh같습니다.

mypath=$0:A

이제 다른 조개에 대한,하지만 realpath()readlink()표준 기능 (후자는 시스템 호출되고)있다, realpath그리고 readlink일부 시스템은 하나 또는 여러 행동과 기능 세트와 다른 또는 둘 다 가지고 있지만, 표준 명령하지 않습니다.

종종 이식성을 위해 다음을 수행 할 수 있습니다 perl.

abs_path() {
  perl -MCwd -le '
    for (@ARGV) {
      if ($p = Cwd::abs_path $_) {
        print $p;
      } else {
        warn "abs_path: $_: $!\n";
        $ret = 1;
      }
    }
    exit $ret' "$@"
}

즉 더 GNU의처럼 행동 것 readlink -f보다는 realpath()(GNU readlink -e파일만큼의 dirname이이처럼 존재하지 않는 경우는 불평하지 않을 것이다에).


참고 :이 기능은 작동하지 않습니다 .zshrc: 대신 이 게시물을 참조하십시오 .
브라이스 Guinta

24

zsh에서는 다음을 수행 할 수 있습니다.

mypath=${0:a}

또는 스크립트가있는 디렉토리를 가져 오려면 다음을 수행하십시오.

mydir=${0:a:h}

출처 : zshexpn (1) 매뉴얼 페이지, HISTORY EXPANSION 섹션, 하위 섹션 수정 자 (또는 간단히 info -f zsh -n Modifiers).


단! 나는 오랫동안 이런 생각을 찾고 있었고 그것을 검색하는 zsh 맨 페이지 전체를 읽었지만 '역사 확장'을 보는 것은 결코 일어나지 않았을 것입니다.
Vucar Timnärakrul

1
GNU와 동등한 readlink -f것은 오히려입니다 $0:A.
Stéphane Chazelas

11

나는 이것을 몇 년 동안 사용 해왔다.

# The absolute, canonical ( no ".." ) path to this script
canonical=$(cd -P -- "$(dirname -- "$0")" && printf '%s\n' "$(pwd -P)/$(basename -- "$0")")

1
나는 그것을 좋아한다! 지금까지는 꽤 이식성이 뛰어납니다. Windows 2008의 Solaris, OmniOS, Linux, Mac 및 Cygwin에서도 작동합니다.
Tim Kennedy

4
GNU와 동등하지 않은 곳 readlink -f은 스크립트 자체가 심볼릭 링크 일 때입니다.
Stéphane Chazelas

with with Ubuntu 16.04 zsh에서 홈 디렉토리 ( /home/ville)에있는 동안 직접 또는 서브 쉘 (제안대로 )을 실행하면 인쇄됩니다 /home/ville/zsh.
Ville

8

이 구문은 (테스트 어떤 Bourne 쉘 스타일의 통역에 이식해야한다 bash, ksh88, ksh93, zsh, mksh, dashbusybox sh) :

mypath=$(exec 2>/dev/null;cd -- $(dirname "$0"); unset PWD; /usr/bin/pwd || /bin/pwd || pwd)
echo mypath=$mypath

이 버전은 기존 AT & T Bourne 쉘 (POSIX 이외)에 호환성을 추가합니다.

mypath=`dirname "$0"`
mypath=`exec 2>/dev/null;(cd -- "$mypath") && cd -- "$mypath"|| cd "$mypath"; unset PWD; /usr/bin/pwd || /bin/pwd || pwd`
echo mypath=$mypath

감사. 그러나 $PWD설정을 해제하는 것은 너무 과도 할 수 있다고 생각합니다 cd -P .. 다음 과 같이 절대 전류로 설정할 수 있습니다 . 나는 그것이 bourneshell에서 작동할지 의심합니다. 그러나 그것은 당신이 처음 테스트 한 모든 것에서 작동해야합니다. 어쨌든 나를 위해 않습니다.
mikeserv

@moose 어떤 OS를 실행하고 있습니까?
jlliagre

누가 무스입니까? 뭐?
mikeserv

@mikeserv 사슴은 몇 가지 문제에 대한 의견을 게시 행인입니다 zshdirnameHIR 철회 신속 / 그녀의 코멘트 ...하지만
jlliagre

Bourne Shell은 cd (1)에 getopt ()를 사용하지 않으므로 Bourne Shell 스크립트는 Bourne Shell에서 작동하지 않습니다.
schily

4

당신이 정말로 절대 경로, 즉 루트 디렉토리의 경로를 의미한다고 가정하면 :

case $0 in
  /*) mypath=$0;;
  *) mypath=$PWD/$0;;
esac

이것은 모든 Bourne 스타일 쉘에서 작동합니다.

모든 심볼릭 링크가 해결 된 경로를 의미한다면 이는 다른 문제입니다. readlink -fLinux (일부 벗겨진 BusyBox 시스템 제외), FreeBSD, NetBSD, OpenBSD 및 Cygwin에서 작동하지만 OS / X, AIX, HP / UX 또는 Solaris에서는 작동하지 않습니다. 가있는 경우 readlink루프에서 호출 할 수 있습니다.

realpath () {
  [ -e "$1" ] || return
  case $1 in
    /*) :;;
    *) set "$PWD/$1";;
  esac
  while [ -L "$1" ]; do
    set "${1%/*}" "$(readlink "$1")"
    case $2 in
      /*) set "$2";;
      *) if [ -z "$1" ]; then set "/$2"; else set "$(cd "$1" && pwd -P)/$2"; fi;;
    esac
  done
  case $1 in
    */.|*/..) set "$(cd "$1" && pwd -P)";;
    */./*|*/../*) set "$(cd "${1%/*}" && pwd -P)/${1##*/}"
  esac
  realpath=$1
}

이 없으면으로 readlink근사 할 수 ls -n있지만 ls파일 이름에서 인쇄 할 수없는 문자를 엉망으로 만들지 않는 경우에만 작동합니다 .

poor_mans_readlink () {
  if [ -L "$1" ]; then
    set -- "$1" "$(LC_ALL=C command ls -n -- "$2"; echo z)"
    set -- "${2%??}"
    set -- "${2#*"$1 -> "}"
  fi
  printf '%s\n' "$1"
}

(추가 기능 z은 링크 대상이 줄 바꿈으로 끝나는 경우 명령 대체가 그렇지 않으면 realpath디렉토리 이름에 대한 경우를 처리하지 않습니다.)


1
출력이 터미널로 이동하지 않을 때 인쇄 할 수없는 문자ls엉망으로 만드는 구현 에 대해 알고 있습니까 ?
Stéphane Chazelas 2016 년

1
@ StéphaneChazelas touch Stéphane; LC_ALL=C busybox ls Stéphane | catSt??phane(이름이 UTF-8 인 경우 latin1은 단일을 제공합니다 ?). 나는 오래된 상용 Unices에서도 그것을 보았다고 생각합니다.
Gilles 'SO- 악마 그만해'

@ StéphaneChazelas 몇 가지 버그를 수정했지만 광범위하게 테스트하지는 않았습니다. 어떤 경우에는 여전히 실패하는지 알려주십시오 (일부 디렉토리에서 실행 권한이 부족한 경우 제외).
Gilles 'SO- 악한 중지'

@Gilles- busybox이게 뭐야? git 에 따르면 busybox ls2011 년부터 코드 변경이 없었습니다. busybox ls2013 년경 My-이 작업을 수행하지 않습니다. 이 - 경 2012 - 않습니다 . 이유를 설명 할 수 있습니다. busyboxwchar 지원을 포함하도록 유니 코드 지원으로 빌드 했습니까 ? mkinitcpio busybox패키지 에서 빌드 옵션을 확인하기 위해 갈 수도 있습니다 .
mikeserv

질-나는 처음 에이 답변을 잘못 판단했다고 생각합니다. 나는 당신의 맹 글링 파일 이름 만트라가 완전히 잘못 되었다고 확신하지만 , poor_mans_readlink 는 매우 잘 수행되었습니다. 편집을하는 친절 함 (모든 편집이 할 것임)과 나중에 핑을한다면, 이에 대한 투표를 취소하고 싶습니다.
mikeserv

1

현재 디렉토리 또는 쉘 스크립트를 실행 한 디렉토리에 대한 실행 권한이있는 경우 디렉토리에 대한 절대 경로를 원하는 경우 cd.

cd의 사양 중 10 단계

-P옵션이 유효 하다면, $PWD환경 변수는로 출력 될 문자열로 설정되어야한다 pwd -P. 현재 작업 디렉토리를 판별하기 위해 새 디렉토리 또는 해당 디렉토리의 상위에 대한 권한이 충분하지 않은 경우 $PWD환경 변수 의 값 이 지정되지 않습니다.

그리고 pwd -P

표준 출력에 기록 된 경로명에는 기호 링크 유형의 파일을 참조하는 구성 요소가 포함되지 않아야합니다. pwd유틸리티가 표준 출력에 쓸 수 있는 여러 경로 이름이있는 경우 (하나는 단일 / 슬래시 문자로 시작하고 하나는 두 개의 / 슬래시 문자로 시작), 단일 / 슬래시 문자로 시작하는 경로 이름을 작성해야합니다. 경로명은 앞뒤에 하나 또는 두 개의 / 슬래시 문자 다음에 불필요한 / 슬래시 문자를 포함하지 않아야합니다.

때문입니다 cd -P무엇 현재 작업 디렉토리 설정해야한다 pwd -P, 그렇지 않으면 인쇄해야을 하고cd -인쇄하는 $OLDPWD그 다음 작품 :

mkdir ./dir
ln -s ./dir ./ln
cd ./ln ; cd . ; cd -

산출

/home/mikeserv/test/ln

좀 기다려...

cd -P . ; cd . ; cd -

산출

/home/mikeserv/test/dir

그리고 인쇄 할 때 cd -인쇄 중 $OLDPWD입니다. 이제 절대 경로가 되 자마자 cd설정 되므로 다른 변수가 필요하지 않습니다. 그리고 사실, 난 후행 필요가 없습니다 하지만 리셋의 특정 행동이 에 때 대화 형 쉘에 꾸밈이됩니다. 따라서 개발하는 것은 좋은 습관입니다.$PWDcd -P . $PWD/.$PWD$HOMEcd

따라서 경로에서 위의 작업을 수행하면 경로 ${0%/*}를 확인하기에 충분해야 $0하지만 $0소프트 링크 인 경우 불행히도 디렉토리를 경로로 변경할 수 없습니다.

이를 처리하는 함수는 다음과 같습니다.

zpath() { cd -P . || return
    _out() { printf "%s$_zdlm\n" "$PWD/${1##*/}"; }
    _cd()  { cd -P "$1" ; } >/dev/null 2>&1
    while [ $# -gt 0 ] && _cd .
    do  if     _cd "$1" 
        then   _out
        elif ! [ -L "$1" ] && [ -e "$1" ] 
        then   _cd "${1%/*}"; _out "$1"
        elif   [ -L "$1" ]
        then   ( while set -- "${1%?/}"; _cd "${1%/*}"; [ -L "${1##*/}" ]
                 do    set " $1" "$(_cd -; ls -nd -- "$1"; echo /)"
                       set -- "${2#*"$1" -> }"
                 done; _out "$1" 
    );  else   ( PS4=ERR:\ NO_SUCH_PATH; set -x; : "$1" )
    fi; _cd -; shift; done
    unset -f _out _cd; unset -v _zdlm                                    
}

서브 쉘을 호출하지 않고 현재 쉘에서 가능한 한 많은 노력을 기울이고 있지만 디렉토리를 가리 키지 않는 오류 및 소프트 링크에 ​​대해 서브 쉘이 호출됩니다. POSIX 호환 쉘과 POSIX 호환 및 ls깨끗한 _function()네임 스페이스에 따라 다릅니다 . 그것은 여전히 ​​후자의 기능없이 잘 작동하지만, unset이 경우 현재의 일부 쉘 기능을 덮어 쓸 수 있습니다 . 일반적으로 이러한 모든 종속성은 Unix 머신에서 상당히 안정적으로 사용할 수 있어야합니다.

인수와 함께 또는 인수없이 호출되는 첫 번째 작업 $PWD은 정식 값으로 재설정 됩니다. 필요한 경우 대상에 대한 링크를 확인합니다. 논증없이 부름을 받았으며 그것에 관한 것입니다. 그러나 그들과 함께 전화하면 각각의 경로를 해결하고 정식화하거나 그렇지 stderr않은 이유에 대한 메시지를 인쇄합니다 .

대부분 현재 쉘에서 작동하기 때문에 모든 길이의 인수 목록을 처리 할 수 ​​있어야합니다. 또한 $_zdlm변수를 찾고 (이unset 변수를 통과 할 때도) 각 이스케이프 된 인수 바로 오른쪽에 C 이스케이프 된 값을 인쇄합니다. 각 인수 뒤에 항상 단일 \newline 문자가옵니다.

그것의 정규 값으로 설정하는 것보다 다른 변화 디렉토리를 많이 수행하지만, 그것은 영향을주지 않습니다 $PWD하지만, $OLDPWD어떤 방법으로 그것을 통해 경우에 따라 계산 될 수 없다.

가능한 빨리 각 인수를 종료하려고합니다. 그것은에 첫번째 시도 cd$1. 가능한 경우 인수의 정식 경로를로 인쇄합니다 stdout. 그것이 $1존재하지 않고 소프트 링크가 아닌지 점검합니다 . 참이면 인쇄합니다.

이런 식 $1으로 디렉토리를 가리 키지 않는 심볼릭 링크가 아닌 한 쉘이 처리 할 수있는 모든 파일 유형 인수를 처리합니다 . 이 경우 while서브 쉘에서 루프를 호출 합니다.

ls링크를 읽기 위해 호출 합니다. 참조 경로를 안정적으로 처리하려면 현재 디렉토리를 먼저 초기 값으로 변경해야합니다. 따라서 명령 대체 서브 쉘에서 함수는 다음을 수행합니다.

cd -...ls...echo /

ls링크의 이름과 문자열을 완전히 포함해야하는만큼 출력 왼쪽에서 제거 합니다 ->. 내가 처음 피하기 위해 시도에서이 작업을 수행하는 동안 shift그리고 $IFS알고 보니 이것은 내가 이해할 수있는 근처로 가장 신뢰할 수있는 방법이다. 이것은 Gilles의 poor_mans_readlink가하는 것과 동일 합니다.

반환 된 파일 이름 ls이 소프트 링크가 아닐 때까지 루프 에서이 프로세스를 반복합니다 . 이 시점에서 이전과 같이 해당 경로를 정규화 cd한 다음 인쇄합니다.

사용법 예 :

zpath \
    /tmp/script \   #symlink to $HOME/test/dir/script.sh
    ln \            #symlink to ./dir/
    ln/nl \         #symlink to ../..
    /dev/fd/0 \     #currently a here-document like : dash <<\HD
    /dev/fd/1 \     #(zlink) | dash
    file \          #regular file                                             
    doesntexist \   #doesnt exist
    /dev/disk/by-path/pci-0000:00:16.2-usb-0:3:1.0-scsi-0:0:0:0 \
    /dev/./././././././null \
    . ..      

산출

/home/mikeserv/test/dir/script.sh
/home/mikeserv/test/dir/
/home/mikeserv/test/
/tmp/zshtpKRVx (deleted)
/proc/17420/fd/pipe:[1782312]
/home/mikeserv/test/file
ERR: NO_SUCH_PATH: doesntexist
/dev/sdd
/dev/null
/home/mikeserv/test/
/home/mikeserv/

아니면 ...

ls
dir/  file  file?  folder/  link@  ln@  script*  script3@  script4@

zdlm=\\0 zpath * | cat -A

산출

/home/mikeserv/test/dir/^@$               
/home/mikeserv/test/file^@$
/home/mikeserv/test/file$
^@$
/home/mikeserv/test/folder/^@$
/home/mikeserv/test/file$               #'link' -> 'file\n'
^@$
/home/mikeserv/test/dir/^@$             #'ln' -> './dir'
/home/mikeserv/test/script^@$
/home/mikeserv/test/dir/script.sh^@$    #'script3' -> './dir/script.sh'
/home/mikeserv/test/dir/script.sh^@$    #'script4' -> '/tmp/script' -> ...

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.