스크립트 자체에서 Bash 스크립트의 소스 디렉토리를 얻는 방법


4949

해당 스크립트 에서 Bash 스크립트가 있는 디렉토리의 경로를 얻으려면 어떻게합니까 ?

Bash 스크립트를 다른 응용 프로그램의 실행기로 사용하고 싶습니다. 작업 디렉토리를 Bash 스크립트가있는 디렉토리로 변경하고 싶으므로 해당 디렉토리의 파일을 다음과 같이 조작 할 수 있습니다.

$ ./application

69
디렉토리 이름 끝에 줄 바꿈 이 있으면 현재 솔루션이 작동하지 않습니다 . 명령 대체로 제거됩니다. 이 문제를 해결하려면 명령 대체-에 줄 바꾸기 문자를 추가하고 명령 대체 DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd && echo x)"없이 제거하십시오 DIR="${DIR%x}".
l0b0

80
@ jpmc26 두 가지 매우 일반적인 상황이 있습니다 : 사고와 파괴. 누군가 어딘가에했기 때문에 스크립트가 예측할 수없는 방식으로 실패해서는 안됩니다 mkdir $'\n'.
l0b0

24
사람들이 그런 식으로 자신의 시스템을 파괴 할 수있게하는 사람은 그러한 문제를 발견하기 위해 그것을 강타 당해서는 안됩니다. 나는 bash를 사용한 25 년 동안 이런 종류의 일이 어디서나 일어나는 것을 본 적이 없었습니다 .... 이것이 우리가 펄과 같은 것들과 오염 검사와 같은 관행을 갖는 이유입니다 (아마도 화를 낼 것입니다 :)
osirisgothra

3
@ l0b0에 동일한 보호가 필요 dirname하고 디렉토리가 -(예 :)로 시작할 수 있다고 가정하십시오 --help. DIR=$(reldir=$(dirname -- "$0"; echo x); reldir=${reldir%?x}; cd -- "$reldir" && pwd && echo x); DIR=${DIR%?x}. 아마도 이것은 과잉입니까?
Score_Under

55
나는 주제에 관한 이 Bash FAQ 를 읽는 것이 좋습니다 .
Rany Albeg Wein

답변:


6567
#!/bin/bash

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"

어디에서 스크립트를 호출하든 스크립트의 전체 디렉토리 이름을 제공하는 유용한 one-liner입니다.

스크립트를 찾는 데 사용 된 경로의 마지막 구성 요소가 심볼릭 링크가 아닌 한 (디렉토리 링크는 정상 임) 작동합니다. 스크립트 자체에 대한 링크도 해결하려면 여러 줄 솔루션이 필요합니다.

#!/bin/bash

SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
  SOURCE="$(readlink "$SOURCE")"
  [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"

이 마지막 하나는 별명의 조합으로 작동 source, bash -c심볼릭 링크 등

주의 : 당신이 경우에 cd이 코드를 실행하기 전에 다른 디렉토리로, 결과가 잘못 될 수 있습니다!

또한 사용자가 cd를 현명하게 재정 의하여 출력을 stderr로 리디렉션하는 경우 ( Mac에서 호출 할 때와 같은 이스케이프 시퀀스 포함) $CDPATHgotchas 및 stderr 출력 부작용 update_terminal_cwd >&2에주의하십시오. 명령 >/dev/null 2>&1끝에 추가하면 cd두 가지 가능성이 모두 해결됩니다.

작동 방식을 이해하려면 다음과 같은 더 자세한 양식을 실행하십시오.

#!/bin/bash

SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  TARGET="$(readlink "$SOURCE")"
  if [[ $TARGET == /* ]]; then
    echo "SOURCE '$SOURCE' is an absolute symlink to '$TARGET'"
    SOURCE="$TARGET"
  else
    DIR="$( dirname "$SOURCE" )"
    echo "SOURCE '$SOURCE' is a relative symlink to '$TARGET' (relative to '$DIR')"
    SOURCE="$DIR/$TARGET" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
  fi
done
echo "SOURCE is '$SOURCE'"
RDIR="$( dirname "$SOURCE" )"
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
if [ "$DIR" != "$RDIR" ]; then
  echo "DIR '$RDIR' resolves to '$DIR'"
fi
echo "DIR is '$DIR'"

그리고 다음과 같이 인쇄됩니다.

SOURCE './scriptdir.sh' is a relative symlink to 'sym2/scriptdir.sh' (relative to '.')
SOURCE is './sym2/scriptdir.sh'
DIR './sym2' resolves to '/home/ubuntu/dotfiles/fo fo/real/real1/real2'
DIR is '/home/ubuntu/dotfiles/fo fo/real/real1/real2'

27
당신과 해결책에 그 작품을 도착 user25866에 의해 답이 접근 방식을 융합 할 수 source <script>bash <script>: DIR="$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)".
Dan Molding

21
때때로 cdSTDOUT에 무언가를 인쇄합니다! 예를 들어, 귀하의 경우 $CDPATH이있다 .. 이 사례를 다루려면DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" > /dev/null && pwd )"
user716468

182
이 대답은 괜찮지 않고 심볼릭 링크와 함께 작동하지 않으며 지나치게 복잡합니다. dirname $(readlink -f $0)올바른 명령입니다. 참조 gist.github.com/tvlooy/cbfbdb111a4ebad8b93e을 테스트 케이스에 대한
tvlooy

167
@tvlooy IMO 경로에 공백이 있으면 실패하기 때문에 대답이 그대로 정확하지 않습니다. 개행 문자와 달리, 이것은 드물거나 드물지 않습니다. dirname "$(readlink -f "$0")"복잡성을 추가하지 않으며 최소한의 문제로 공정한 측정을 할 수 있습니다.
Adrian Günter

10
@tvlooy 귀하의 의견은 macOS (또는 일반적으로 BSD)와 호환되지 않지만 허용되는 답변은 다릅니다. readlink -f $0제공합니다 readlink: illegal option -- f.
Alexander Ljungberg

875

사용 dirname "$0":

#!/bin/bash
echo "The script you are running has basename `basename "$0"`, dirname `dirname "$0"`"
echo "The present working directory is `pwd`"

사용 pwd당신이이에 포함 된 디렉토리에서 스크립트를 실행하지 않으면 작동하지 않습니다 만.

[matt@server1 ~]$ pwd
/home/matt
[matt@server1 ~]$ ./test2.sh
The script you are running has basename test2.sh, dirname .
The present working directory is /home/matt
[matt@server1 ~]$ cd /tmp
[matt@server1 tmp]$ ~/test2.sh
The script you are running has basename test2.sh, dirname /home/matt
The present working directory is /tmp

25
bash 이상의 이식성을 위해 $ 0만으로는 충분하지 않을 수 있습니다. 명령이 경로에서 발견 된 경우이 작업을 수행하려면 "type -p $ 0"을 대체해야 할 수도 있습니다.
Darron

10
@Darron : type -p스크립트가 실행 가능한 경우 에만 사용할 수 있습니다 . 스크립트를 사용하여 실행 bash test2.sh하고 다른 이름으로 실행 가능한 동일한 이름을 가진 다른 스크립트가있는 경우 미묘한 구멍을 열 수도 있습니다.
D.Shawley

90
@ Darron : 그러나 질문에 태그가 bash있고 해시 뱅 라인이 명시 적으로 언급 했기 때문에 bashism /bin/bash에 의존하는 것이 안전하다고 말할 것입니다.
Joachim Sauer

34
+1이지만 사용시 문제 dirname $0는 디렉토리가 현재 디렉토리 인 경우 얻을 수 있다는 것 .입니다. 스크립트에서 디렉토리를 변경하지 않고 dirname $0절대 경로 처럼 경로를 사용할 것으로 예상하지 않는 한 괜찮습니다 . : 절대 경로를 얻으려면 pushd `dirname $0` > /dev/null, SCRIPTPATH=`pwd`, popd > /dev/null: pastie.org/1489386을 (그러나 반드시 해당 경로를 확장 할 수있는 더 좋은 방법이?)
TJ 크라우

9
@TJ Crowder dirname $0변수에 변수를 할당하고 스크립트를 실행하는 데 사용할 경우 문제가 확실하지 않습니다 $dir/script.sh. 이것이 90 %의 시간 동안 이런 유형의 유스 케이스라고 생각합니다. ./script.sh잘 작동합니다.
matt b

515

dirname명령은 가장 기본적이며 단순히 $0(스크립트 이름) 변수 에서 파일 이름까지의 경로를 구문 분석합니다 .

dirname "$0"

그러나 매트 b가 지적했듯이 반환되는 경로는 스크립트 호출 방법에 따라 다릅니다. pwd스크립트가있는 디렉토리가 아니라 현재 디렉토리 만 알려주기 때문에 작업을 수행하지 않습니다. 또한 스크립트에 대한 심볼릭 링크가 실행되면 (아마도 상대적인) 경로를 얻게됩니다 실제 스크립트가 아닌 링크가있는 위치

다른 사람들은 readlink명령 을 언급 했지만 가장 간단하게 다음을 사용할 수 있습니다.

dirname "$(readlink -f "$0")"

readlink스크립트 경로를 파일 시스템의 루트에서 절대 경로로 해석합니다. 따라서 단일 또는 이중 도트, 물결표 및 / 또는 기호 링크가 포함 된 경로는 전체 경로로 해석됩니다.

다음은 이들 각각을 보여주는 스크립트입니다 whatdir.sh.

#!/bin/bash
echo "pwd: `pwd`"
echo "\$0: $0"
echo "basename: `basename $0`"
echo "dirname: `dirname $0`"
echo "dirname/readlink: $(dirname $(readlink -f $0))"

상대 경로를 사용하여 내 홈 디렉토리 에서이 스크립트를 실행하십시오.

>>>$ ./whatdir.sh 
pwd: /Users/phatblat
$0: ./whatdir.sh
basename: whatdir.sh
dirname: .
dirname/readlink: /Users/phatblat

다시 말하지만 스크립트의 전체 경로를 사용하십시오.

>>>$ /Users/phatblat/whatdir.sh 
pwd: /Users/phatblat
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat

이제 디렉토리 변경 :

>>>$ cd /tmp
>>>$ ~/whatdir.sh 
pwd: /tmp
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat

마지막으로 심볼릭 링크를 사용하여 스크립트를 실행하십시오.

>>>$ ln -s ~/whatdir.sh whatdirlink.sh
>>>$ ./whatdirlink.sh 
pwd: /tmp
$0: ./whatdirlink.sh
basename: whatdirlink.sh
dirname: .
dirname/readlink: /Users/phatblat

13
readlink기본 설치의 일부 플랫폼에서는 사용할 수 없습니다. 가능하다면 사용을 피하십시오
TL

43
공백 문제를 피하기 위해 모든 것을 인용하도록주의하십시오 :export SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
Catskul

13
OSX에서는 Yosemite 10.10.1이에 -f대한 옵션으로 인식되지 않습니다 readlink. stat -f대신 사용 하면 작업이 수행됩니다. 감사합니다
cucu8

11
OSX에는 greadlink기본적으로 readlink모두 친숙한가 있습니다. 다음은 플랫폼 독립 버전입니다.dir=`greadlink -f ${BASH_SOURCE[0]} || readlink -f ${BASH_SOURCE[0]}`
robert

6
잘 부탁드립니다, @robert. 참고로, greadlinkhomebrew를 통해 쉽게 설치할 수 있습니다 :brew install coreutils
phatblat

184
pushd . > /dev/null
SCRIPT_PATH="${BASH_SOURCE[0]}"
if ([ -h "${SCRIPT_PATH}" ]); then
  while([ -h "${SCRIPT_PATH}" ]); do cd `dirname "$SCRIPT_PATH"`; 
  SCRIPT_PATH=`readlink "${SCRIPT_PATH}"`; done
fi
cd `dirname ${SCRIPT_PATH}` > /dev/null
SCRIPT_PATH=`pwd`;
popd  > /dev/null

다음을 포함한 모든 버전에서 작동

  • 다중 깊이 소프트 링크를 통해 호출 될 때
  • 때 파일
  • 스크립트 " source"일명 .(점) 연산자로 스크립트를 호출 한 경우
  • $0호출자가 arg 를 수정 한 경우
  • "./script"
  • "/full/path/to/script"
  • "/some/path/../../another/path/script"
  • "./some/folder/script"

또는, bash는 스크립트 자체가 경우 상대 심볼릭 링크 당신이 원하는 의 전체 경로를 그것을 따라 반환이 연결된에 스크립트 :

pushd . > /dev/null
SCRIPT_PATH="${BASH_SOURCE[0]}";
if ([ -h "${SCRIPT_PATH}" ]) then
  while([ -h "${SCRIPT_PATH}" ]) do cd `dirname "$SCRIPT_PATH"`; SCRIPT_PATH=`readlink "${SCRIPT_PATH}"`; done
fi
cd `dirname ${SCRIPT_PATH}` > /dev/null
SCRIPT_PATH=`pwd`;
popd  > /dev/null

SCRIPT_PATH어떻게 호출 되든 전체 경로로 제공됩니다.
스크립트 시작시이 위치를 찾으십시오.

이 설명과 코드는 GPL2.0 이상 또는 CC-SA 3.0 (CreativeCommons Share Alike) 이상에서 선택 가능한 라이센스 인 Copyleft입니다. (c) 2008. 모든 권리 보유. 어떤 종류의 보증도하지 않습니다. 경고를 받았습니다.
http://www.gnu.org/licenses/gpl-2.0.txt
http://creativecommons.org/licenses/by-sa/3.0/
18eedfe1c99df68dc94d4a94712a71aaa8e1e9e36cacf421b9463dd2bbaa02906d0d6656


4
좋은! "pushd [...] popd / dev / null"을 SCRIPT_PATH =로 바꾸면 더 짧아 질 수 있습니다 readlink -f $(dirname "${VIRTUAL_ENV}").
전자 위성

5
그리고 pushed를 사용하는 대신 ...; $ (cd dirname "${SCRIPT_PATH}"&& pwd) 를 사용하는 것이 낫지 않습니까? 그러나 어쨌든 위대한 대본!
ovanes

6
나중에 다시 cd전화 를 걸기 위해 스크립트가 현재 디렉토리 를 벗어나는 것은 위험합니다. 스크립트 cd는 디렉토리를 호출 할 때 현재 디렉토리로 다시 변경할 수있는 권한이 없을 수 있습니다. (동일한 푸시 / 팝에 간다)
Adrian Pronk

7
readlink -fGNU 전용입니다. BSD readlink에는 그 옵션이 없습니다.
Kara Brightwell

3
불필요한 서브 쉘은 무엇입니까? ([ ... ])보다 효율성이 떨어지며 [ ... ], 여기에서 그 성능에 대한 대가로 제공되는 격리를 사용하는 이점이 없습니다.
Charles Duffy

110

짧은 답변:

`dirname $0`

또는 ( 바람직하게는 ) :

$(dirname "$0")

17
스크립트를 소싱하면 작동하지 않습니다. "source my / script.sh"
Arunprasad Rajkumar

나는 이것을 bash 스크립트에서 항상 사용하여 물건을 자동화하고 종종 같은 디렉토리에서 다른 스크립트를 호출합니다. 내가 사용하지 않겠다고 source이들과 cd $(dirname $0)기억하기 쉬운.
kqw

16
@vidstige : ${BASH_SOURCE[0]}대신에 $0작동source my/script.sh
Timothy Jones

@TimothyJones는 bash 이외의 다른 쉘에서 소스를 얻는 경우 100 % 실패합니다. ${BASH_SOURCE[0]}전혀 만족스럽지 않습니다. ${BASH_SOURCE:-0}훨씬 낫다.
Mathieu CAROFF

106

당신은 사용할 수 있습니다 $BASH_SOURCE:

#!/bin/bash

scriptdir=`dirname "$BASH_SOURCE"`

Bash 확장이므로 사용 #!/bin/bash하지 않아야 #!/bin/sh합니다.


14
내가 할 때 ./foo/script, 다음 $(dirname $BASH_SOURCE)입니다 ./foo.
까지

1
@Till,이 경우 realpath명령을 사용 하여 ./foo/script의 전체 경로를 얻을 수 있습니다 . 그래서 dirname $(realpath ./foo/script) 스크립트의 경로를 제공 할 것입니다.
purushothaman poovai

73

이것은해야합니다 :

DIR="$(dirname "$(readlink -f "$0")")"

이것은 경로의 심볼릭 링크 및 공백과 함께 작동합니다.

dirname및에 대한 매뉴얼 페이지를 참조하십시오 readlink.

댓글 트랙에서 Mac OS에서는 작동하지 않는 것 같습니다. 왜 그런지 모르겠습니다. 어떤 제안?


6
솔루션과 같이 스크립트를 호출 ./script.sh.대신 전체 디렉토리 경로
브루노 Negrão ZICA

5
MacOS에는 readlink에 -f 옵션이 없습니다. stat대신 사용하십시오 . 그러나 여전히 .'this'디렉토리에 있는지 보여줍니다 .
Denis The Menace

2
coreutilsHomebrew 에서 설치 하고 MacOS greadlink에서 -f옵션 을 얻는 데 사용해야 합니다 . Linux가 아니라 덮개 아래 * BSD이기 때문입니다.
dragon788

오른쪽을 둘러싼 큰 따옴표를 추가해야합니다.DIR="$(dirname "$(readlink -f "$0")")"
hagello

60

pwd현재 작업 디렉토리를 찾기 위해, 그리고 사용할 수있는 dirname특정 파일의 디렉토리 찾기 (실행 된 명령이다 $0, 그래서 dirname $0당신에게 현재 스크립트의 디렉토리를 제공해야합니다).

그러나 dirname파일 이름의 디렉토리 부분을 정확하게 지정하면 현재 작업 디렉토리와 관련이 없을 가능성이 높습니다. 어떤 이유로 스크립트가 디렉토리를 변경해야하는 경우 출력 dirname은 의미가 없습니다.

나는 다음을 제안한다.

#!/bin/bash

reldir=`dirname $0`
cd $reldir
directory=`pwd`

echo "Directory is $directory"

이런 식으로 상대 디렉토리가 아닌 절대 디렉토리를 얻습니다.

스크립트는 별도의 bash 인스턴스에서 실행되므로 나중에 작업 디렉토리를 복원 할 필요는 없지만 어떤 이유로 스크립트를 다시 변경하려는 경우 pwd변수 값을 변수에 쉽게 할당 할 수 있습니다. 나중에 사용할 수 있도록 디렉토리를 변경하십시오.

그냥

cd `dirname $0`

질문의 특정 시나리오를 해결하면 더 일반적으로 더 유용한 방법을 찾을 수 있습니다.


9
DIRECTORY = $ (cd dirname $0&& pwd)
dogbane

스크립트가 다른 스크립트를 제공하고 후자의 이름을 알고 자하는 경우에는 작동하지 않습니다.
reinierpost

52

허용 된 답변에 한 줄짜리 붙여 넣기를 복사하기 위해이 페이지에 여러 번 와서 피곤합니다. 그 문제는 이해하고 기억하기 쉽지 않다는 것입니다.

다음은 기억하기 쉬운 스크립트입니다.

DIR="$(dirname "${BASH_SOURCE[0]}")"  # get the directory name
DIR="$(realpath "${DIR}")"    # resolve its full path if need be

2
또는 한 줄로 더 모호하게 : DIR=$(realpath "$(dirname "${BASH_SOURCE[0]}")")
agc

이것이 왜 대답이 맞지 않습니까? realpath루프를 사용하여 "수동으로"해결 하는 것과 차이 가 readlink있습니까? readlink매뉴얼 페이지 에서도Note realpath(1) is the preferred command to use for canonicalization functionality.
User9123

1
그건 그렇고 우리는 realpath전에 dirname가 아니라 전에 신청 해서는 안됩니까? 스크립트 파일 자체가 심볼릭 링크 인 경우 ... 그것은 뭔가를 줄 것이다 DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")". 실제로 Simon이 제안한 답변에 매우 가깝습니다.
User9123

@ User9123 수락하는 것이 인기있는 모든 쉘 / 디스트로와 호환되도록 노력한다고 생각합니다. 더 나아가, 당신이하려는 일에 따라 대부분의 경우 사람들은 실제 소스의 디렉토리 대신 symlink가있는 디렉토리를 얻으려고합니다.

37

나는 이것이 다른 사람들이 그렇게 쉬운 것처럼 생각하지 않습니다. pwd현재 디렉토리가 반드시 스크립트가있는 디렉토리는 아니기 때문에 작동하지 않습니다. $0항상 정보가있는 것은 아닙니다. 스크립트를 호출하는 다음 세 가지 방법을 고려하십시오.

./script

/usr/bin/script

script

첫 번째와 세 번째 방법 $0에는 전체 경로 정보가 없습니다. 두 번째와 세 번째에서는 pwd작동하지 않습니다. 세 번째 방법으로 디렉토리를 얻는 유일한 방법은 경로를 통해 실행하고 일치하는 파일을 찾는 것입니다. 기본적으로 코드는 OS의 기능을 다시 수행해야합니다.

원하는 것을 수행하는 한 가지 방법은 /usr/share디렉토리 의 데이터를 하드 코딩 하고 전체 경로로 참조하는 것입니다. /usr/bin어쨌든 데이터는 디렉토리에 없어야 하므로 아마도해야 할 일입니다.


9
그의 의견을 반증하려는 경우, 스크립트가 코드 예제와 함께 저장된 위치에 액세스 할 수 있는지 증명하십시오.
Richard Duerr

34
SCRIPT_DIR=$( cd ${0%/*} && pwd -P )

이것은 선택한 답변보다 훨씬 짧습니다. 그리고 잘 작동하는 것 같습니다. 이것은 사람들이 그것을 간과하지 않도록 1000 표를 받아야합니다.
Patrick

2
이전의 많은 답변들이 자세하게 설명 되었 듯이 , 스크립트가 어떻게 호출되는지에 따라 올바른 정보를 가지고 있거나 보장 할 수 $0없습니다 pwd.
IMSoP

34
$(dirname "$(readlink -f "$BASH_SOURCE")")

나는 bash에 능숙하지 않은 독자들조차도 명시 적이기 때문에 $BASH_SOURCEover를 선호한다 $0. $(dirname -- "$(readlink -f -- "$BASH_SOURCE")")
blobmaster

32

Mac OS X 10.6.6에서 현재 작업 디렉토리를 가져옵니다.

DIR=$(cd "$(dirname "$0")"; pwd)

27

이것은 Linux마다 다르지만 다음을 사용할 수 있습니다.

SELF=$(readlink /proc/$$/fd/255)

1
또한 배쉬 특정이지만 배쉬의 동작이 변경 되었습니까? /proc/fd/$$/255디렉토리가 아닌 tty를 가리키는 것으로 보입니다. 예를 들어, 현재 로그인 셸에서 파일 설명자 0, 1, 2 및 255는 모두를 참조합니다 /dev/pts/4. 어쨌든 bash 매뉴얼에는 fd 255가 언급되어 있지 않으므로 아마도이 동작에 의존하는 것이 현명하지 않을 것입니다. \
Keith Thompson

2
대화식 쉘! = 스크립트. 어쨌든 realpath ${BASH_SOURCE[0]};가장 좋은 방법 인 것 같습니다.
Steve Baker

23

POSIX 호환 단일 라이너는 다음과 같습니다.

SCRIPT_PATH=`dirname "$0"`; SCRIPT_PATH=`eval "cd \"$SCRIPT_PATH\" && pwd"`

# test
echo $SCRIPT_PATH

4
스크립트 자체를 실행하거나 sudo를 사용하여이 작업을 수행했지만 source ./script.sh를 호출 할 때는 성공하지 못했습니다.
Michael R

cd새 경로 이름을 인쇄하도록 구성 하면 실패합니다 .
Aaron Digulla

18

나는 이것들을 모두 시도했지만 아무도 효과가 없었다. 하나는 매우 가까웠지만 작은 버그로 인해 심하게 부러졌습니다. 그들은 경로를 따옴표로 묶는 것을 잊었습니다.

또한 많은 사람들이 쉘에서 스크립트를 실행한다고 가정하므로 새 스크립트를 열면 기본값이 집으로 설정됩니다.

이 디렉토리를 사용해보십시오.

/var/No one/Thought/About Spaces Being/In a Directory/Name/And Here's your file.text

이것은 어디서 어떻게 실행하는지에 관계없이 올바르게 가져옵니다.

#!/bin/bash
echo "pwd: `pwd`"
echo "\$0: $0"
echo "basename: `basename "$0"`"
echo "dirname: `dirname "$0"`"

실제로 유용하게 사용하려면 다음을 실행중인 스크립트의 디렉토리로 변경하는 방법이 있습니다.

cd "`dirname "$0"`"

4
스크립트가 다른 스크립트에서 제공되는 경우 작동하지 않습니다.
reinierpost

$ 0의 마지막 부분이 다른 디렉토리의 항목을 가리키는 기호 링크 인 경우에는 작동하지 않습니다 ( ln -s ../bin64/foo /usr/bin/foo).
hagello

17

간단하고 올바른 방법은 다음과 같습니다.

actual_path=$(readlink -f "${BASH_SOURCE[0]}")
script_dir=$(dirname "$actual_path")

설명:

  • ${BASH_SOURCE[0]}-스크립트의 전체 경로. 이 값은 스크립트가 소싱되는 경우에도 정확합니다 (예 : bashsource <(echo 'echo $0') 인쇄). 대신 스크립트의 전체 경로를 인쇄합니다. (물론, 이것은 Bash에 의존하는 것이 좋다고 가정합니다.)${BASH_SOURCE[0]}

  • readlink -f-지정된 경로의 모든 심볼릭 링크를 재귀 적으로 해결합니다. 이것은 GNU 확장이며 BSD 시스템에서는 사용할 수 없습니다. Mac을 사용하는 경우 Homebrew를 사용하여 GNU를 설치 coreutils하고로 대체 할 수 있습니다 greadlink -f.

  • 물론 dirname경로의 상위 디렉토리를 가져옵니다.


1
greadlink -f불행히도 효율적으로 작동하지 않습니다 source:( Mac에서 스크립트를 보내고
게이브 Kopley

17

가장 짧고 우아한 방법은 다음과 같습니다.

#!/bin/bash
DIRECTORY=$(cd `dirname $0` && pwd)
echo $DIRECTORY

이것은 모든 플랫폼에서 작동하며 매우 깨끗합니다.

자세한 내용은 " bash 스크립트가있는 디렉토리는 무엇입니까? " 에서 찾을 수 있습니다 .


훌륭한 깨끗한 솔루션이지만 파일이 심볼 링크되어 있으면 작동하지 않습니다.
ruuter

16

나는 이와 같은 것을 사용할 것이다 :

# retrieve the full pathname of the called script
scriptPath=$(which $0)

# check whether the path is a link or not
if [ -L $scriptPath ]; then

    # it is a link then retrieve the target path and get the directory name
    sourceDir=$(dirname $(readlink -f $scriptPath))

else

    # otherwise just get the directory name of the script path
    sourceDir=$(dirname $scriptPath)

fi

이것이 진짜입니다! 간단하게 작동합니다 sh! 간단한 dirname "$0"기반 솔루션의 문제점 : 스크립트가 $PATH경로에 있고 경로없이 호출되면 잘못된 결과를 제공합니다.
Notinlist

@Notinlist 그렇지 않습니다. 스크립트가를 통해 발견되면 PATH, $0절대 파일 이름을 포함합니다. 스크립트가 포함 된 상대 또는 절대 파일 이름으로 호출되면 /, $0이 포함됩니다.
Neil Mayhew

소스 스크립트에서는 작동하지 않습니다.
Naidu를 Amit

16

이 지적 솔루션 전자 satis 및 3bcdnlklvc04a에 약간의 수정입니다 그들의 대답 :

SCRIPT_DIR=''
pushd "$(dirname "$(readlink -f "$BASH_SOURCE")")" > /dev/null && {
    SCRIPT_DIR="$PWD"
    popd > /dev/null
}    

이것은 나열된 모든 사례에서 여전히 작동합니다.

이 방지됩니다 popdA가 실패한 후 pushd, konsolebox 덕분에.


이것은 심볼릭 링크 이름이 아닌 "실제"디렉토리 이름을 얻는 데 완벽하게 작동합니다. 감사합니다!
Jay Taylor

1
더 나은SCRIPT_DIR=''; pushd "$(dirname "$(readlink -f "$BASH_SOURCE")")" > /dev/null && { SCRIPT_DIR=$PWD; popd > /dev/null; }
Konsolebox

@konsolebox, 무엇을 방어하려고합니까? 나는 일반적으로 논리 조건부 인라인의 팬이지만 푸시에서 보았던 특정 오류는 무엇입니까? 빈 SCRIPT_DIR을 반환하는 대신 직접 처리하는 방법을 찾고 싶습니다.
Fuwjax 2019 년

@Fuwjax (실제로 popd드물더라도) pushd실패 하는 경우 를 피하기위한 자연스런 연습 . 그리고 pushd실패 할 경우 , 당신의 가치는 SCRIPT_DIR무엇 이라고 생각 하십니까? 논리적으로 보일 수있는 것 또는 한 사용자가 선호 할 수있는 것에 따라 동작이 다를 수 있지만 확실하지 않은 popd것은 잘못된 것입니다.
konsolebox

이러한 모든 pushd popd위험은 단순히 삭제하고 대신 명령 대체에 cd+를 사용하여 피할 수 있습니다 pwd. SCRIPT_DIR=$(...)
Naidu를 Amit

16

GNU coreutils가있는 시스템 readlink(예 : Linux) :

$(readlink -f "$(dirname "$0")")

스크립트 파일 이름을 포함 BASH_SOURCE할 때 사용할 필요가 없습니다 $0.


2
스크립트가로 제공되지 않는 한. 또는 'source'인 경우 여전히 스크립트에서 소스로 사용하거나 명령 줄에서 '-bash'(tty login) 또는 'bash'( 'bash -l'을 통해 호출) 또는 '/ bin / bash '(대화 형 비 로그인 셸로 호출)
osirisgothra

dirname전화 에 두 번째 따옴표 쌍을 추가했습니다 . 디렉토리 경로에 공백이 있으면 필요합니다.
user1338062

14
#!/bin/sh
PRG="$0"

# need this for relative symlinks
while [ -h "$PRG" ] ; do
   PRG=`readlink "$PRG"`
done

scriptdir=`dirname "$PRG"`

다른 시스템에서 테스트하지 않았습니다. 그러나이 솔루션은 적어도 우분투에서 즉시 작동하는 솔루션입니다.
나 투스 드류

$0소스 스크립트에서는 작동하지 않습니다
Amit Naidu

13

$_의 대안으로 언급 할 가치가 $0있습니다. Bash에서 스크립트를 실행하는 경우 허용되는 답변을 다음과 같이 단축 할 수 있습니다.

DIR="$( dirname "$_" )"

이것은 스크립트에서 첫 번째 문장이어야합니다.


4
당신 source이나 .스크립트 라면 깨집니다 . 이러한 상황에서는 $_이전에 실행 한 마지막 명령의 마지막 매개 변수가 포함 .됩니다. $BASH_SOURCE매번 작동합니다.
clacke

11

나는 주어진 많은 답변을 비교하고 더 작은 솔루션을 생각해 냈습니다. 이들은 당신이 좋아하는 조합에서 발생하는 모든 미친 가장자리 사례를 처리하는 것처럼 보입니다.

  • 절대 경로 또는 상대 경로
  • 파일 및 디렉토리 소프트 링크
  • 호출로 script, bash script, bash -c script, source script, 또는. script
  • 디렉토리 및 / 또는 파일 이름의 공백, 탭, 줄 바꿈, 유니 코드 등
  • 하이픈으로 시작하는 파일 이름

Linux에서 실행중인 경우 proc핸들 을 사용하는 것이 현재 실행중인 스크립트의 완전히 해결 된 소스를 찾는 최상의 솔루션 인 것 같습니다 (대화식 세션에서 링크는 각각을 가리킴 /dev/pts/X).

resolved="$(readlink /proc/$$/fd/255 && echo X)" && resolved="${resolved%$'\nX'}"

여기에는 약간의 추악함이 있지만 수정은 작고 이해하기 쉽습니다. 우리는 bash 프리미티브 만 사용하지 않지만 readlink작업을 상당히 단순화 하기 때문에 괜찮습니다 . 는 echo X을 추가 X파일 이름에 후행 공백을 먹게되지 않으며, 매개 변수 대체되도록 변수 문자열의 끝에 ${VAR%X}줄 끝에이 제거됩니다 X. readlink줄 바꿈을 추가 하기 때문에 (이전 속임수가 아닌 경우 일반적으로 명령 대체에서 먹게 됨) 우리도 그 줄을 제거해야합니다. 이것은 $''인용 체계를 사용하여 가장 쉽게 수행되며, 다음 과 같은 이스케이프 시퀀스를 사용할 수 있습니다.\n 줄 바꿈을 나타냅니다 (이것은 또한 이름이 지정된 디렉토리와 파일을 쉽게 만들 수있는 방법입니다).

위의 내용은 Linux에서 현재 실행중인 스크립트를 찾는 데 필요한 사항을 다루지 만 proc파일 시스템이 없거나 다른 파일의 완전히 해결 된 경로를 찾으려고하면 아마도 아래 코드가 도움이되는 것을 찾으십시오. 위의 한 줄짜리에서 약간 수정 된 것입니다. 당신이 이상한 디렉토리 / 파일 이름과 함께 놀아 모두 출력을 확인하는 경우 lsreadlink같이, 유익한 ls대체 경로 "단순화"가 출력 ?줄 바꿈 같은 것들에 대해.

absolute_path=$(readlink -e -- "${BASH_SOURCE[0]}" && echo x) && absolute_path=${absolute_path%?x}
dir=$(dirname -- "$absolute_path" && echo x) && dir=${dir%?x}
file=$(basename -- "$absolute_path" && echo x) && file=${file%?x}

ls -l -- "$dir/$file"
printf '$absolute_path: "%s"\n' "$absolute_path"

내가 얻을 /dev/pts/30우분투 14.10 데스크톱에 떠들썩한 파티와 함께.
Dan Dascalescu

@DanDascalescu 하나의 라이너를 사용합니까? 아니면 하단에 전체 코드 스 니펫이 있습니까? 까다로운 경로 이름을 먹이셨습니까?
billyjmc

한 라인 플러스의 또 다른 라인 echo $resolved내가로 저장, d, chmod +x d, ./d.
Dan Dascalescu

@DanDascalescu는 스크립트에서 첫 번째 줄은 할 필요가#!/bin/bash
billyjmc

10

다음을 사용하십시오.

real=$(realpath $(dirname $0))

1
내가 알고 싶은 것은 왜이 방법이 좋지 않습니까? 나에게 나쁘지 않은 것처럼 보였습니다. 왜 다운 다운 된 이유를 설명 할 수 있습니까?
Shou Ya

7
realpath는 표준 유틸리티가 아닙니다.
Steve Bennett

2
Linux에서 realpath는 표준 유틸리티 (GNU coreutils 패키지의 일부)이지만 bash 내장 기능이 아닙니다 (예 : bash 자체에서 제공하는 기능). 당신이 리눅스를 실행하는 경우,이 메소드는 아마 일, 나는 대체 줄지라도 $0위해 ${BASH_SOURCE[0]}이 방법이 기능을 포함하여, 어디에서나 작동 할 수 있도록.
더그 리처드슨

3
이 답변의 작업 순서가 잘못되었습니다. 당신은 필요 첫째 , 해결 심볼릭 링크를 다음 수행 dirname의 마지막 부분이 있기 때문에 $0심볼릭 링크가 될 수 있음을 자체를 심볼릭 링크와 같은 디렉토리에없는 파일을 가리키는. 이 답변에 설명 된 솔루션은 대상 디렉토리가 아닌 심볼릭 링크가 저장된 디렉토리의 경로를 가져옵니다. 또한이 솔루션에는 인용이 없습니다. 경로에 특수 문자가 포함되어 있으면 작동하지 않습니다.
hagello

3
dir="$(realpath "$(dirname "${BASH_SOURCE[0]}")")"
Kostiantyn Ponomarenko

9

다음과 같은 상호 호환 솔루션을 사용해보십시오.

CWD="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"

(운영 체제에 따라) realpath또는 readlink사용할 수없는 명령으로 .

참고 : Bash에서는을 ${BASH_SOURCE[0]}대신 사용 하는 것이 좋습니다 $0. 그렇지 않으면 파일 ( source/ .)을 소싱 할 때 경로가 중단 될 수 있습니다 .

또는 bash에서 다음 기능을 시도 할 수 있습니다.

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

이 함수는 1 개의 인수를 갖습니다. 인수에 이미 절대 경로가 있으면 그대로 인쇄하고, 그렇지 않으면 $PWD변수 + 파일 이름 인수 ( ./접두사 없이 )를 인쇄 하십시오 .

관련 :


realpath 함수에 대해 더 자세히 설명하십시오.
Chris

1
@Chris realpath함수는 1 개의 인수를 갖습니다 . 인수에 이미 절대 경로가 있으면 그대로 인쇄하고, 그렇지 않으면 접두사 $PWD없이 + 파일 이름을 인쇄하십시오 ./.
kenorb

스크립트가 심볼릭 링크되어 있으면 상호 호환 솔루션이 작동하지 않습니다.
Jakub Jirutka

9

나는 이것을 가지고 있다고 믿는다. 나는 파티에 늦었지만 일부 사람들 이이 스레드를 발견하면 여기에 있다는 것에 감사 할 것이라고 생각합니다. 의견은 다음을 설명해야합니다.

#!/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

8

다음은 스크립트 정보를 얻는 간단한 방법입니다.

폴더 및 파일 :

    Script: "/tmp/src dir/test.sh"
    Calling folder: "/tmp/src dir/other"

다음 명령을 사용하십시오.

    echo Script-Dir : `dirname "$(realpath $0)"`
    echo Script-Dir : $( cd ${0%/*} && pwd -P )
    echo Script-Dir : $(dirname "$(readlink -f "$0")")
    echo
    echo Script-Name : `basename "$(realpath $0)"`
    echo Script-Name : `basename $0`
    echo
    echo Script-Dir-Relative : `dirname "$BASH_SOURCE"`
    echo Script-Dir-Relative : `dirname $0`
    echo
    echo Calling-Dir : `pwd`

그리고 나는이 출력을 얻었다 :

     Script-Dir : /tmp/src dir
     Script-Dir : /tmp/src dir
     Script-Dir : /tmp/src dir

     Script-Name : test.sh
     Script-Name : test.sh

     Script-Dir-Relative : ..
     Script-Dir-Relative : ..

     Calling-Dir : /tmp/src dir/other

참조 : https://pastebin.com/J8KjxrPF



간단한 작업 판을 찾기가 어렵 기 때문에 제 대답은 괜찮습니다. 여기에서 cd ​​+ pwd, dirname + realpath 또는 dirname + readlink와 같은 코드를 사용할 수 있습니다. 모든 부품이 이전에 존재하는지 확실하지 않으며 대부분의 답변이 복잡하고 과부하되어 있습니다. 여기서 당신이 사용하고 싶은 코드를 찾아 낼 수 있습니다. 최소한 앞으로는 필요에 따라 제거하지 마십시오. D
User8461

8

이것은 bash-3.2에서 작동합니다.

path="$( dirname "$( which "$0" )" )"

~/bin디렉토리 $PATHA있으면이 디렉토리 안에 있습니다. 스크립트를 제공 ~/bin/lib/B합니다. 포함 된 스크립트가 lib서브 디렉토리 에서 원래 스크립트와 관련된 위치는 알지만 사용자의 현재 디렉토리와 관련된 위치는 알 수 없습니다.

이것은 (내부 A)에 의해 해결됩니다 .

source "$( dirname "$( which "$0" )" )/lib/B"

사용자의 위치 나 스크립트 호출 방식에 관계없이 항상 작동합니다.


3
요점 which은 매우 논쟁의 여지가 있습니다. type, hash및 기타 내장은 bash에서 동일한 작업을 더 잘 수행합니다. tcsh와 같은 다른 쉘에서 사용되는 which것과 동일하지는 않지만 이식성이 뛰어납니다 which.
Monica Reinstate Please Please

"항상"? 전혀. which외부 도구이기 때문에 부모 쉘과 동일하게 작동한다고 믿을 이유가 없습니다.
Charles Duffy

7

내 관점에서 가장 컴팩트 한 솔루션은 다음과 같습니다.

"$( cd "$( echo "${BASH_SOURCE[0]%/*}" )"; pwd )"

Bash 이외의 것에 의존하지 않습니다. 의 사용 dirname, readlink그리고 basename가능한 모든의 경우가 가장 피해야되도록 결국, 호환성 문제로 이어질 것입니다.


2
아마도 슬래시를 추가해야합니다 : "$( cd "$( echo "${BASH_SOURCE[0]%/*}/" )"; pwd )". 그렇지 않으면 루트 디렉토리에 문제가 있습니다. 또한 왜 에코를 사용해야합니까?
konsolebox

dirname그리고 basenamePOSIX는 왜 피하고을 사용하여, 표준화? 링크 : dirname,basename
myrdd

두 가지 추가 프로세스 포크를 방지하고 쉘 내장을 고수하는 것이 한 가지 이유 일 수 있습니다.
Naidu를 Amit
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.