유닉스 쉘 스크립트는 스크립트 파일이 어느 디렉토리에 있는지 알아?


511

기본적으로 쉘 스크립트 파일 위치와 관련된 경로로 스크립트를 실행해야합니다. 현재 디렉토리를 스크립트 파일이있는 디렉토리와 동일한 디렉토리로 변경하려면 어떻게해야합니까?


12
정말 중복인가요? 이 질문은 "유닉스 쉘 스크립트"에 관한 것이고 다른 하나는 Bash에 관한 것입니다.
michaeljt 2016 년

2
@BoltClock :이 질문은 부적절하게 닫혔습니다. 관련 질문은 Bash에 관한 것입니다. 이 질문은 유닉스 쉘 프로그래밍에 관한 것입니다. 허용되는 답변은 매우 다릅니다!
Dietrich Epp

@ Dietrich Epp : 네 말이 맞아. asker가 허용 된 답변을 선택하고 [bash] 태그를 추가하면 (아마도 그에 대한 응답으로) 플래그에 대한 응답으로 질문을 복제본으로 표시하게되었습니다.
BoltClock

2
이 답변이 더 낫다고 생각합니다 . Bash 스크립트의 소스 디렉토리 가져 오기
Alan Zhiliang Feng

답변:


568

Bash에서는 다음과 같이 필요한 것을 얻습니다.

#!/usr/bin/env bash

BASEDIR=$(dirname "$0")
echo "$BASEDIR"

17
다른 디렉토리의 심볼릭 링크를 통해 스크립트를 호출 한 경우에는 작동하지 않습니다. 이 작업을 수행하려면 다음 사항 readlink도 함께 사용해야 합니다 (아래 al의 답변 참조)
AndrewR

44
bash에서는 스크립트를 '소싱'할 때와 같이 호출되는 스크립트의 경로가 항상 포함되어 있지 않기 때문에 $BASH_SOURCE대신에 사용하는 것이 더 안전합니다 . $0$0
mklement0

2
$BASH_SOURCEBash에 관한 것입니다. 질문은 일반적으로 쉘 스크립트에 관한 것입니다.
Ha-Duong Nguyen

7
@ auraham : CUR_PATH=$(pwd)또는 pwd현재 디렉토리를 반환하십시오 (스크립트 부모 디렉토리 일 필요는 없습니다)!
Andreas Dietrich

5
@ mklement0 권장 방법을 사용하여 시도하고 $BASH_SOURCE필요한 것을 반환합니다. 내 스크립트는 다른 스크립트에서 호출 및되고 $0반환 .하면서 $BASH_SOURCE수익률 (내 경우에는 적절한 하위 디렉토리 scripts).
David Rissato Cruz

401

원래 게시물에는 솔루션이 포함되어 있습니다 (응답을 무시하고 유용한 것은 추가하지 마십시오). 흥미로운 작업은 언급 된 unix 명령 readlink과 옵션으로 수행됩니다 -f. 스크립트가 상대 경로뿐만 아니라 절대 경로에 의해 호출 될 때 작동합니다.

bash의 경우 sh, ksh :

#!/bin/bash 
# Absolute path to this script, e.g. /home/user/bin/foo.sh
SCRIPT=$(readlink -f "$0")
# Absolute path this script is in, thus /home/user/bin
SCRIPTPATH=$(dirname "$SCRIPT")
echo $SCRIPTPATH

tcsh의 경우 csh :

#!/bin/tcsh
# Absolute path to this script, e.g. /home/user/bin/foo.csh
set SCRIPT=`readlink -f "$0"`
# Absolute path this script is in, thus /home/user/bin
set SCRIPTPATH=`dirname "$SCRIPT"`
echo $SCRIPTPATH

참조 : https : //.com/a/246128/59087


12
참고 : 모든 시스템에이있는 것은 아닙니다 readlink. 그렇기 때문에 푸시 / 팝 (bash에 내장)을 사용하는 것이 좋습니다.
docwhat

23
OS X (Lion) 및 BSD에서 다른 작업 -freadlink수행 하는 옵션 입니다. stackoverflow.com/questions/1055671/…
Ergwun

9
@Ergwun의 의견을 명확히하기 위해 : -fOS X에서는 전혀 지원되지 않습니다 (사자로서). -f예를 들어 pushd "$(dirname "$(readlink "$BASH_SOURCE" || echo "$BASH_SOURCE")")", 최대 한 수준의 간접적 해결을 통해 할 수있는 일을 포기 하거나 링크 된 게시물에 설명 된대로 자체 재귀 symlink-following 스크립트를 롤업 할 수 있습니다.
mklement0

2
OP가 절대 경로가 필요한 이유를 여전히 이해하지 못합니다. "."보고 중 스크립트 경로와 관련된 파일에 액세스하고 ./myscript.sh와 같은 스크립트를 호출 한 경우 올바르게 작동해야합니다.
Stefan Haberl

10
@StefanHaberl 나는 당신의 현재 작업 디렉토리가 예를 들어, 스크립트의 위치 (다른 동안 스크립트를 실행 한 경우는 문제가 될 것 같아 sh /some/other/directory/script.sh),이 경우 .귀하의 비밀번호가 아닌 것/some/other/directory
존 z를

51

답변에 대한 이전의 의견이 그것을 말했지만 다른 모든 답변 중에서 놓치기 쉽습니다.

bash를 사용할 때 :

echo this file: "$BASH_SOURCE"
echo this dir: "$(dirname "$BASH_SOURCE")"

Bash 참조 매뉴얼, 5.2 Bash 변수


3
이 중 하나만 환경 경로에서 스크립트로 작동하며 가장 많이 투표 한 스크립트는 작동하지 않습니다. 감사합니다!
7월

dirname "$BASH_SOURCE"대신 $ BASH_SOURCE에서 공백을 처리하는 데 사용해야 합니다.
Mygod

1
도구 ShellCheck에 따르면 디렉토리를 인쇄하는보다 명확한 방법은 "$ (dirname"$ {BASH_SOURCE {0}} ")입니다. BASH_SOURCE는 배열이고 아래 첨자가 없으면 첫 번째 요소가 기본적으로 사용되기 때문입니다. .
Adrian M.

1
@AdrianM. 색인에 대괄호가 아닌 대괄호가 필요합니다."$(dirname "${BASH_SOURCE[0]}")"
Hashbrown

나에게 좋지 않다 ..prints a dot (즉, 현재 디렉토리, 아마도)
JL_SO

40

bash를 사용한다고 가정합니다.

#!/bin/bash

current_dir=$(pwd)
script_dir=$(dirname $0)

echo $current_dir
echo $script_dir

이 스크립트는 사용자가있는 디렉토리를 인쇄 한 다음 스크립트가있는 디렉토리를 인쇄해야합니다. 예를 들어 /에서 스크립트 를 사용하여 호출 /home/mez/하면 출력됩니다.

/
/home/mez

명령의 출력에서 변수를 할당 할 때,에서 명령을 포장 기억 $(하고 )또는 당신이 원하는 출력을받지 않습니다 -.


3
현재 디렉토리에서 스크립트를 호출하면 작동하지 않습니다.
Eric Wang

1
@EricWang 당신은 항상 현재 디렉토리에 있습니다.
ctrl-alt-delor

나에게 $ current_dir은 실제로 스크립트를 호출하는 경로입니다. 그러나 $ script_dir은 스크립트의 디렉토리가 아니며 단지 점입니다.
Michael

31

bash를 사용하는 경우 ....

#!/bin/bash

pushd $(dirname "${0}") > /dev/null
basedir=$(pwd -L)
# Use "pwd -P" for the path without links. man bash for more info.
popd > /dev/null

echo "${basedir}"

4
당신은 대체 할 수있는 pushd/를 popd함께 cd $(dirname "${0}")하고 cd -그들이이있는 경우는, 다른 쉘에서 작동하도록 pwd -L.
docwhat

왜 밀고 튀어 나와서 사용하겠습니까?
qodeninja

1
따라서 원래 디렉토리를 변수에 저장할 필요가 없습니다. 함수 등에서 많이 사용하는 패턴입니다. 정말 잘 중첩되어 있습니다.
docwhat

변수가 스크립트에서 참조되는지 여부에 관계없이 메모리에 변수에 저장됩니다. 또한 푸시 및 팝 실행 비용이 CPU 사이클 및 가독성 모두에서 스크립트에서 로컬 Bash 변수를 만들지 않는 비용보다 훨씬 큽니다.
ingyhere

20

TheMarko가 제안한 바와 같이 :

BASEDIR=$(dirname $0)
echo $BASEDIR

스크립트가있는 동일한 디렉토리에서 스크립트를 실행하지 않으면 '.'값을 얻지 않는 한 작동합니다.

이 문제를 해결하려면 다음을 사용하십시오.

current_dir=$(pwd)
script_dir=$(dirname $0)

if [ $script_dir = '.' ]
then
script_dir="$current_dir"
fi

이제 스크립트 전체에서 current_dir 변수를 사용하여 스크립트 디렉토리를 참조 할 수 있습니다. 그러나 여전히 symlink 문제가있을 수 있습니다.


20

이 질문에 대한 가장 좋은 대답은 여기에서 대답했습니다 :
Bash 스크립트의 소스 디렉토리 가져 오기

그리고 그건:

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

스크립트는 어디에서 호출 되든지 스크립트의 전체 디렉토리 이름을 제공합니다.

작동 방식을 이해하려면 다음 스크립트를 실행할 수 있습니다.

#!/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" )" && pwd )"
if [ "$DIR" != "$RDIR" ]; then
  echo "DIR '$RDIR' resolves to '$DIR'"
fi
echo "DIR is '$DIR'"


10

POSIX oneliner로 만들어 봅시다 :

a="/$0"; a=${a%/*}; a=${a#/}; a=${a:-.}; BASEDIR=$(cd "$a"; pwd)

BSD 쉘을 포함한 많은 Bourne 호환 쉘에서 테스트되었습니다.

내가 아는 한, 나는 저자이며 공개 도메인에 넣었습니다. 자세한 내용은 https://www.jasan.tk/posts/2017-05-11-posix_shell_dirname_replacement/를 참조하십시오.


1
cd: too many arguments경로에 공백이 있으면 작성된대로를 반환합니다 $PWD. (명쾌한 수정이지만 실제로 얼마나 많은 사례가 있는지 보여줍니다)
michael

1
공감할 것입니다. @michael의 주석을 제외하고 경로에 공백이 있으면 실패합니다 ... 그 해결 방법이 있습니까?
spechter

1
@ spechter 예, 그에 대한 해결책이 있습니다. 업데이트 된 jasan.tk/posix/2017/05/11/posix_shell_dirname_replacement
Ján Sáreník

@Der_Meister-더 구체적으로 작성하십시오. 또는 jasan.tk에서 jasan에게 이메일을 작성 (및 암호화 가능)
Ján Sáreník

2
ln -s / home / der / 1 / test / home / der / 2 / test && / home / der / 2 / test => / home / der / 2 (원본 스크립트의 경로를 대신 표시해야 함)
Der_Meister

10
BASE_DIR="$(cd "$(dirname "$0")"; pwd)";
echo "BASE_DIR => $BASE_DIR"

3
내가 아는 가장 신뢰할 수있는 비 bash 특정 방식.
eddygeek

9

실제 스크립트 디렉토리를 얻으려면 (symlink를 사용하여 스크립트를 호출하는지 또는 직접 호출하는지에 관계없이) 다음을 시도하십시오.

BASEDIR=$(dirname $(realpath "$0"))
echo "$BASEDIR"

이것은 리눅스와 macOS 모두에서 작동합니다. 에 대해 언급 한 사람이 없습니다 realpath. 이 방법에 단점이 있는지 확실하지 않습니다.

macOS에서는을 ( coreutils를) 사용 하려면 설치 해야합니다 realpath. 예 : brew install coreutils.


6

소개

이 답변 은 The Thread에서 가장 깨졌지만 충격적으로 가장 많이 투표 된 답변을 수정 합니다 (TheMarko가 작성).

#!/usr/bin/env bash

BASEDIR=$(dirname "$0")
echo "$BASEDIR"

왜 "$ 0"이라는 dirname을 사용하면 작동하지 않습니까?

dirname $ 0 은 사용자가 매우 특정한 방식으로 스크립트를 시작한 경우에만 작동합니다. 이 답변이 실패하고 스크립트가 충돌하는 몇 가지 상황을 찾을 수있었습니다.

우선,이 답변이 어떻게 작동하는지 이해합시다. 그는 다음을 수행하여 스크립트 디렉토리를 얻습니다.

dirname "$0"

$ 0 은 스크립트를 호출하는 명령의 첫 부분을 나타냅니다 (기본적으로 인수없이 입력 된 명령입니다).

/some/path/./script argument1 argument2

$ 0 = "/ some / path /./ script"

dirname은 기본적으로 문자열에서 마지막 /를 찾아서 잘립니다. 따라서 할 경우 :

  dirname /usr/bin/sha256sum

당신은 얻을 것이다 : / usr / bin

이 예제는 / ​​usr / bin / sha256sum이 올바른 형식의 경로이지만

  dirname "/some/path/./script"

잘 작동하지 않고 당신에게 줄 것입니다 :

  BASENAME="/some/path/." #which would crash your script if you try to use it as a path

스크립트와 같은 디렉토리에 있고이 명령으로 스크립트를 시작한다고 가정 해보십시오.

./script   

이 상황에서 $ 0은 ./script이며 dirname $ 0은 다음을 제공합니다.

. #or BASEDIR=".", again this will crash your script

사용 :

sh script

전체 경로를 입력하지 않으면 BASEDIR = "도 제공됩니다."

상대 디렉토리 사용 :

 ../some/path/./script

다음과 같은 dirname $ 0를 제공합니다.

 ../some/path/.

/ some 디렉토리에 있고 이런 방식으로 스크립트를 호출하는 경우 (처음에는 /가없고 다시 상대 경로가 있음에 유의) :

 path/./script.sh

dirname $ 0에 대해이 값을 얻습니다.

 path/. 

./path/./script (상대 경로의 다른 형식)는 다음을 제공합니다.

 ./path/.

basedir $ 0 이 작동 하는 유일한 두 가지 상황 은 사용자가 sh 또는 touch를 사용하여 스크립트를 시작하는 경우입니다.

 $0=/some/path/script

dirname과 함께 사용할 수있는 경로를 제공합니다.

해결책

위에서 언급 한 상황을 모두 파악하고 감지하고 발생하는 경우 수정 사항을 적용하십시오.

#!/bin/bash
#this script will only work in bash, make sure it's installed on your system.

#set to false to not see all the echos
debug=true

if [ "$debug" = true ]; then echo "\$0=$0";fi


#The line below detect script's parent directory. $0 is the part of the launch command that doesn't contain the arguments
BASEDIR=$(dirname "$0") #3 situations will cause dirname $0 to fail: #situation1: user launches script while in script dir ( $0=./script)
                                                                     #situation2: different dir but ./ is used to launch script (ex. $0=/path_to/./script)
                                                                     #situation3: different dir but relative path used to launch script
if [ "$debug" = true ]; then echo 'BASEDIR=$(dirname "$0") gives: '"$BASEDIR";fi                                 

if [ "$BASEDIR" = "." ]; then BASEDIR="$(pwd)";fi # fix for situation1

_B2=${BASEDIR:$((${#BASEDIR}-2))}; B_=${BASEDIR::1}; B_2=${BASEDIR::2}; B_3=${BASEDIR::3} # <- bash only
if [ "$_B2" = "/." ]; then BASEDIR=${BASEDIR::$((${#BASEDIR}-1))};fi #fix for situation2 # <- bash only
if [ "$B_" != "/" ]; then  #fix for situation3 #<- bash only
        if [ "$B_2" = "./" ]; then
                #covers ./relative_path/(./)script
                if [ "$(pwd)" != "/" ]; then BASEDIR="$(pwd)/${BASEDIR:2}"; else BASEDIR="/${BASEDIR:2}";fi
        else
                #covers relative_path/(./)script and ../relative_path/(./)script, using ../relative_path fails if current path is a symbolic link
                if [ "$(pwd)" != "/" ]; then BASEDIR="$(pwd)/$BASEDIR"; else BASEDIR="/$BASEDIR";fi
        fi
fi

if [ "$debug" = true ]; then echo "fixed BASEDIR=$BASEDIR";fi

4

이 one-liner는 쉘 스크립트의 위치를 ​​알려줍니다. 스크립트 를 실행했는지 또는 소스했는지는 중요하지 않습니다 . 또한 다음과 같은 경우 관련된 모든 심볼릭 링크를 해결합니다.

dir=$(dirname $(test -L "$BASH_SOURCE" && readlink -f "$BASH_SOURCE" || echo "$BASH_SOURCE"))

그건 그렇고, 당신이 / bin / bash 사용한다고 가정합니다 .


2

너무 많은 답변, 모두 그럴듯하며 각각 각각 장단점 및 약간 다른 목표 (각각 언급해야 함). 다음은 모든 bash에서 모든 시스템에서 명확하고 작동하는 기본 목표를 충족시키는 다른 솔루션입니다 (bash 버전 readlink또는 pwd옵션 에 대한 가정은 없음 ). 흥미로운 문제이지만 일반적으로 실제로 원하는 것은 아닙니다), 경로의 공백 등과 같은 가장자리 사례를 처리하고 오류가 무시되고 문제가있는 경우 정상적인 기본값을 사용합니다.

각 구성 요소는 개별적으로 사용할 수있는 별도의 변수에 저장됩니다.

# script path, filename, directory
PROG_PATH=${BASH_SOURCE[0]}      # this script's name
PROG_NAME=${PROG_PATH##*/}       # basename of script (strip path)
PROG_DIR="$(cd "$(dirname "${PROG_PATH:-$PWD}")" 2>/dev/null 1>&2 && pwd)"


-4

그 트릭을해야합니다 :

echo `pwd`/`dirname $0`

호출 된 방식과 cwd에 따라보기 흉하게 보일 수 있지만 어디로 가야하는지 (또는 모양이 마음에 든다면 문자열을 조정할 수 있습니다).


1
stackoverflow 탈출 문제가 여기에 : 그것은 반드시 다음과 같이 보일 것입니다 : `pwd`/`dirname $0`하지만 여전히 심볼릭 링크에서 실패 할 수 있습니다
Andreas Dietrich
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.