Bash 스크립트가 자신의 전체 경로를 가져 오는 신뢰할 수있는 방법 [중복]
이 질문에 이미 답변이 있습니다.
전체 경로를 알아야하는 Bash 스크립트가 있습니다. 나는 상대적이거나 펑키 한 경로로 끝나지 않고 광범위하게 호환되는 방법을 찾으려고 노력하고 있습니다. sh, csh 등이 아닌 Bash 만 지원하면됩니다.
지금까지 찾은 내용 :
을 통해 스크립트 경로를 가져 오는 주소 내에서 Bash 스크립트의 소스 디렉토리 가져 오기에 대한 허용 된 대답
dirname $0
은 괜찮지 만 상대 경로 (예 :)를 반환 할 수 있습니다.
. 이는 디렉토리를 변경하려는 경우 문제입니다 스크립트를 작성하고 경로가 여전히 스크립트의 디렉토리를 가리 키도록합니다. 그래도dirname
퍼즐의 일부가 될 것입니다.OS X 에서 Bash 스크립트 절대 경로에 대한 허용 된 대답 (OS X에 따라 다르지만 대답은 상관없이 작동 함) 은
$0
상대적으로 보이는지 여부를 확인 하고 앞에 추가$PWD
할 것인지 테스트하는 기능을 제공 합니다. 그러나 결과는 여전히 상대적인 비트를 가질 수 있습니다 (전체적으로는 절대적 임에도 불구하고). 예를 들어 스크립트가t
디렉토리에/usr/bin
있고 사용자가/usr
입력bin/../bin/t
하여 실행하는 경우 (예, 복잡합니다) 다음과/usr/bin/../bin
같이 끝납니다. 스크립트의 디렉토리 경로. 어떤 작품 ,하지만 ...이 페이지 의
readlink
솔루션 은 다음과 같습니다.# Absolute path to this script. /home/user/bin/foo.sh SCRIPT=$(readlink -f $0) # Absolute path this script is in. /home/user/bin SCRIPTPATH=`dirname $SCRIPT`
그러나
readlink
POSIX가 아니며 분명히 솔루션은readlink
BSD가 어떤 이유로 작동하지 않는 GNU에 의존합니다 (확인할 BSD와 같은 시스템에 액세스 할 수 없습니다).
따라서 다양한 방법으로 수행하지만 모두 경고가 있습니다.
더 나은 방법은 무엇입니까? "더 좋음"은 다음을 의미합니다.
- 나에게 절대 경로를 제공합니다.
- 복잡한 방식으로 호출되는 경우에도 펑키 비트를 제거합니다 (위의 # 2에 대한 주석 참조). (예 : 적어도 적당히 경로를 정규화합니다.)
- Bash-ism 또는 * nix 시스템 (GNU / Linux, BSD 및 OS X와 같은 BSD 유사 시스템 등)의 가장 인기있는 풍미에 거의 확실한 것에 만 의존합니다.
- 가능한 경우 외부 프로그램 호출을 피합니다 (예 : Bash 내장 기능 선호).
- ( 업데이트 됨 , 고마워요, wich ) 심볼릭 링크를 해결할 필요가 없습니다 (사실 나는 그것들을 그대로 두는 것을 선호하지만 요구 사항은 아닙니다).
다음은 제가 생각 해낸 것입니다 (편집 : 추가로 sfstewman , levigroker , Kyle Strand 및 Rob Kennedy가 제공 한 일부 조정 ), 대부분 내 "더 나은"기준에 맞는 것 같습니다.
SCRIPTPATH="$( cd "$(dirname "$0")" ; pwd -P )"
이 SCRIPTPATH
선은 특히 원형 교차로처럼 보이지만 SCRIPTPATH=`pwd`
공간과 심볼릭 링크를 적절하게 처리하기 위해 필요합니다 .
또한 액세스 가능한 파일 시스템의 파일에서 나오지 않는 스크립트를 실행하는 것과 같은 난해한 상황 (완벽하게 가능함)은 거기에 맞지 않습니다 (또는 내가 본 다른 답변에서 ).
이 realpath
명령이 여기에 언급되지 않은 것에 놀랐습니다 . 내 이해는 널리 이식 가능 / 이식된다는 것입니다.
초기 솔루션은 다음과 같습니다.
SCRIPT=`realpath $0`
SCRIPTPATH=`dirname $SCRIPT`
그리고 기호 링크를 기본 설정에 따라 해결되지 않은 상태로 두려면 :
SCRIPT=`realpath -s $0`
SCRIPTPATH=`dirname $SCRIPT`
내가 배쉬에서 전체 정식 경로를 얻을 발견하는 가장 간단한 방법은 사용하는 것입니다 cd
및 pwd
:
ABSOLUTE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/$(basename "${BASH_SOURCE[0]}")"
${BASH_SOURCE[0]}
대신을 사용 $0
하면 스크립트가 <name>
또는 로 호출되는지 여부에 관계없이 동일한 동작 이 생성됩니다 source <name>
.
오늘이 문제를 다시 살펴보고 스크립트 자체 내에서 Bash 스크립트의 소스 디렉토리 가져 오기를 찾았 습니다 .
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
연결된 답변에는 더 많은 변형이 있습니다. 예를 들어 스크립트 자체가 심볼릭 링크 인 경우입니다.
쉘 스크립트의 절대 경로 얻기
-f
readlink 의 옵션을 사용하지 않으므로 BSD / Mac OS X에서 작동합니다.
지원
- source ./script (
.
점 연산자에 의해 호출되는 경우 ) - 절대 경로 / path / to / script
- ./script와 같은 상대 경로
- /path/dir1/../dir2/dir3/../script
- 심볼릭 링크에서 호출 될 때
- 심볼릭 링크가 중첩 된 경우 예)
foo->dir1/dir2/bar bar->./../doe doe->script
- 호출자가 스크립트 이름을 변경할 때
이 코드가 작동하지 않는 코너 케이스를 찾고 있습니다 . 알려주세요.
암호
pushd . > /dev/null
SCRIPT_PATH="${BASH_SOURCE[0]}";
while([ -h "${SCRIPT_PATH}" ]); do
cd "`dirname "${SCRIPT_PATH}"`"
SCRIPT_PATH="$(readlink "`basename "${SCRIPT_PATH}"`")";
done
cd "`dirname "${SCRIPT_PATH}"`" > /dev/null
SCRIPT_PATH="`pwd`";
popd > /dev/null
echo "srcipt=[${SCRIPT_PATH}]"
echo "pwd =[`pwd`]"
알려진 issus
스크립트 는 디스크 어딘가에 있어야합니다 . 네트워크를 통해 두십시오. PIPE에서이 스크립트를 실행하려고하면 작동하지 않습니다.
wget -o /dev/null -O - http://host.domain/dir/script.sh |bash
기술적으로 말하면 정의되지 않았습니다. 실제로 이것을 감지하는 건전한 방법은 없습니다. (공동 프로세스는 부모의 환경에 액세스 할 수 없습니다.)
사용하다:
SCRIPT_PATH=$(dirname `which $0`)
which
전달 된 인수가 쉘 프롬프트에 입력되었을 때 실행되었을 실행 파일의 전체 경로를 표준 출력에 인쇄합니다 ($ 0에 포함 된 내용).
dirname
파일 이름에서 디렉토리가 아닌 접미사를 제거합니다.
따라서 경로가 지정되었는지 여부에 관계없이 스크립트의 전체 경로로 끝납니다.
내 Linux 시스템에 realpath가 기본적으로 설치되어 있지 않으므로 다음이 작동합니다.
SCRIPT="$(readlink --canonicalize-existing "$0")"
SCRIPTPATH="$(dirname "$SCRIPT")"
$SCRIPT
스크립트에 대한 실제 파일 경로와 $SCRIPTPATH
스크립트가 포함 된 디렉토리의 실제 경로가 포함됩니다.
이것을 사용하기 전에이 답변 의 주석을 읽으십시오 .
읽기 쉬운? 아래는 대안입니다. 심볼릭 링크를 무시합니다.
#!/bin/bash
currentDir=$(
cd $(dirname "$0")
pwd
)
echo -n "current "
pwd
echo script $currentDir
몇 년 전에 위의 답변을 게시 한 이후로 저는 심 링크를 적절하게 처리하는이 Linux 특정 패러다임을 사용하는 방식으로 발전했습니다.
ORIGIN=$(dirname $(readlink -f $0))
이 질문에 매우 늦게 답변했지만 다음을 사용합니다.
SCRIPT=$( readlink -m $( type -p $0 )) # Full path to script
BASE_DIR=`dirname ${SCRIPT}` # Directory script is run in
NAME=`basename ${SCRIPT}` # Actual name of script even if linked
간단히:
BASEDIR=$(readlink -f $0 | xargs dirname)
멋진 연산자는 필요하지 않습니다.
우리는 자유롭고 방해받지 않는 커뮤니티 사용을 위해 GitHub에 자체 제품 realpath-lib 를 배치했습니다 .
뻔뻔한 플러그이지만이 Bash 라이브러리를 사용하면 다음을 수행 할 수 있습니다.
get_realpath <absolute|relative|symlink|local file>
이 기능은 라이브러리의 핵심입니다.
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
}
외부 종속성이 필요하지 않으며 Bash 4+ 만 필요합니다. 또한 함수를 포함하는 get_dirname
, get_filename
, get_stemname
및validate_path validate_realpath
. 무료이고, 깨끗하고, 간단하고, 잘 문서화되어 있으므로 학습 목적으로도 사용할 수 있으며 의심 할 여지없이 개선 할 수 있습니다. 여러 플랫폼에서 사용해보십시오.
업데이트 : 몇 가지 검토 및 테스트 후 위의 함수를 동일한 결과 (dirname을 사용하지 않고 순수 Bash 만 사용)를 달성하지만 효율성이 더 높은 것으로 대체했습니다.
function get_realpath() {
[[ ! -f "$1" ]] && return 1 # failure : file does not exist.
[[ -n "$no_symlinks" ]] && local pwdp='pwd -P' || local pwdp='pwd' # do symlinks.
echo "$( cd "$( echo "${1%/*}" )" 2>/dev/null; $pwdp )"/"${1##*/}" # echo result.
return 0 # success
}
여기에는 no_symlinks
물리적 시스템에 대한 심볼릭 링크를 확인하는 기능을 제공 하는 환경 설정도 포함 됩니다. 기본적으로 심볼릭 링크를 그대로 유지합니다.
다음 변수를 정의 할 수 있습니다.
CWD="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
또는 Bash에서 다음 기능을 시도 할 수 있습니다.
realpath () {
[[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}
이 함수는 하나의 인수를 사용합니다. 인수에 이미 절대 경로가 있으면 그대로 인쇄하고, 그렇지 않으면 $PWD
변수 + 파일 이름 인수 ( ./
접두사 없이 )를 인쇄 합니다 .
관련 :
Bourne 셸 ( sh
) 준수 방식 :
SCRIPT_HOME=`dirname $0 | while read a; do cd $a && pwd && break; done`
이 문제를 다시 고려하면이 스레드 내에서 참조되는 매우 인기있는 솔루션이 여기에 있습니다 .
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
dirname을 사용했기 때문에이 솔루션을 사용하지 않았습니다. 특히 보안상의 이유로 스크립트를 잠 가야하는 경우 플랫폼 간 문제가 발생할 수 있습니다. 그러나 순수한 Bash 대안으로 다음을 사용하는 것은 어떻습니까?
DIR="$( cd "$( echo "${BASH_SOURCE[0]%/*}" )" && pwd )"
이것이 옵션일까요?
If we use Bash I believe this is the most convenient way as it doesn't require calls to any external commands:
THIS_PATH="${BASH_SOURCE[0]}";
THIS_DIR=$(dirname $THIS_PATH)
The accepted solution has the inconvenient (for me) to not be "source-able":
if you call it from a "source ../../yourScript
", $0
would be "bash
"!
The following function (for bash >= 3.0) gives me the right path, however the script might be called (directly or through source
, with an absolute or a relative path):
(by "right path", I mean the full absolute path of the script being called, even when called from another path, directly or with "source
")
#!/bin/bash
echo $0 executed
function bashscriptpath() {
local _sp=$1
local ascript="$0"
local asp="$(dirname $0)"
#echo "b1 asp '$asp', b1 ascript '$ascript'"
if [[ "$asp" == "." && "$ascript" != "bash" && "$ascript" != "./.bashrc" ]] ; then asp="${BASH_SOURCE[0]%/*}"
elif [[ "$asp" == "." && "$ascript" == "./.bashrc" ]] ; then asp=$(pwd)
else
if [[ "$ascript" == "bash" ]] ; then
ascript=${BASH_SOURCE[0]}
asp="$(dirname $ascript)"
fi
#echo "b2 asp '$asp', b2 ascript '$ascript'"
if [[ "${ascript#/}" != "$ascript" ]]; then asp=$asp ;
elif [[ "${ascript#../}" != "$ascript" ]]; then
asp=$(pwd)
while [[ "${ascript#../}" != "$ascript" ]]; do
asp=${asp%/*}
ascript=${ascript#../}
done
elif [[ "${ascript#*/}" != "$ascript" ]]; then
if [[ "$asp" == "." ]] ; then asp=$(pwd) ; else asp="$(pwd)/${asp}"; fi
fi
fi
eval $_sp="'$asp'"
}
bashscriptpath H
export H=${H}
The key is to detect the "source
" case and to use ${BASH_SOURCE[0]}
to get back the actual script.
Perhaps the accepted answer to the following question may be of help.
How can I get the behavior of GNU's readlink -f on a Mac?
Given that you just want to canonicalize the name you get from concatenating $PWD and $0 (assuming that $0 is not absolute to begin with), just use a series of regex replacements along the line of abs_dir=${abs_dir//\/.\//\/}
and such.
Yes, I know it looks horrible, but it'll work and is pure Bash.
Try this:
cd $(dirname $([ -L $0 ] && readlink -f $0 || echo $0))
Just for the hell of it I've done a bit of hacking on a script that does things purely textually, purely in Bash. I hope I caught all the edge cases.
Note that the ${var//pat/repl}
that I mentioned in the other answer doesn't work since you can't make it replace only the shortest possible match, which is a problem for replacing /foo/../
as e.g. /*/../
will take everything before it, not just a single entry. And since these patterns aren't really regexes I don't see how that can be made to work. So here's the nicely convoluted solution I came up with, enjoy. ;)
By the way, let me know if you find any unhandled edge cases.
#!/bin/bash
canonicalize_path() {
local path="$1"
OIFS="$IFS"
IFS=$'/'
read -a parts < <(echo "$path")
IFS="$OIFS"
local i=${#parts[@]}
local j=0
local back=0
local -a rev_canon
while (($i > 0)); do
((i--))
case "${parts[$i]}" in
""|.) ;;
..) ((back++));;
*) if (($back > 0)); then
((back--))
else
rev_canon[j]="${parts[$i]}"
((j++))
fi;;
esac
done
while (($j > 0)); do
((j--))
echo -n "/${rev_canon[$j]}"
done
echo
}
canonicalize_path "/.././..////../foo/./bar//foo/bar/.././bar/../foo/bar/./../..//../foo///bar/"
I have used the following approach successfully for a while (not on OS X though), and it only uses a shell built-in and handles the 'source foobar.sh' case as far as I have seen.
One issue with the (hastily put together) example code below is that the function uses $PWD which may or may not be correct at the time of the function call. So that needs to be handled.
#!/bin/bash
function canonical_path() {
# Handle relative vs absolute path
[ ${1:0:1} == '/' ] && x=$1 || x=$PWD/$1
# Change to dirname of x
cd ${x%/*}
# Combine new pwd with basename of x
echo $(pwd -P)/${x##*/}
cd $OLDPWD
}
echo $(canonical_path "${BASH_SOURCE[0]}")
type [
type cd
type echo
type pwd
Yet another way to do this:
shopt -s extglob
selfpath=$0
selfdir=${selfpath%%+([!/])}
while [[ -L "$selfpath" ]];do
selfpath=$(readlink "$selfpath")
if [[ ! "$selfpath" =~ ^/ ]];then
selfpath=${selfdir}${selfpath}
fi
selfdir=${selfpath%%+([!/])}
done
echo $selfpath $selfdir
One liner
`dirname $(realpath $0)`
More simply, this is what works for me:
MY_DIR=`dirname $0`
source $MY_DIR/_inc_db.sh
'program story' 카테고리의 다른 글
인수와 매개 변수의 차이점은 무엇입니까? (0) | 2020.10.02 |
---|---|
Visual Studio 내에서 프로젝트 폴더의 이름을 바꾸는 방법은 무엇입니까? (0) | 2020.10.02 |
Android 오류 : 기기에 * .apk를 설치하지 못했습니다. * : 시간 초과 (0) | 2020.09.30 |
Python 3에서 문자열을 바이트로 변환하는 가장 좋은 방법은 무엇입니까? (0) | 2020.09.30 |
문자열을 대문자로 변경하는 방법 (0) | 2020.09.30 |