sourced Shell 스크립트가 자신의 경로를 찾는 방법이 있습니까? tcsh를 사용하는 동료가 있지만 주로 bash에 관심이 있습니다.
소싱으로 인해 현재 셸에서 명령이 실행되므로 운이 좋지 않을 수 있습니다. $0
는 여전히 소스 스크립트가 아닌 현재 쉘의 호출입니다. 내 생각은 현재 source $script $script
이므로 첫 번째 위치 매개 변수에 필요한 정보가 포함됩니다. 더 나은 방법이 있습니까?
분명히, 나는 스크립트를 실행하지 않고 sourcing 스크립트입니다.
source foo.bash
tcsh
, $_
파일 시작시 스크립트 시작 부분의 _는 위치를 포함하고 $0
실행 된 경우 포함합니다.
#!/bin/tcsh
set sourced=($_)
if ("$sourced" != "") then
echo "sourced $sourced[2]"
endif
if ("$0" != "tcsh") then
echo "run $0"
endif
배쉬에서 :
#!/bin/bash
[[ $0 != $BASH_SOURCE ]] && echo "Script is being sourced" || echo "Script is being run"
$BASH_SOURCE
변수. 실행 된 경로를 반환합니다.
[email protected] ~ $ /home/pbm/a.sh
/home/pbm/a.sh
[email protected] ~ $ ./a.sh
./a.sh
[email protected] ~ $ source /home/pbm/a.sh
/home/pbm/a.sh
[email protected] ~ $ source ./a.sh
./a.sh
따라서 다음 단계에서는 경로가 상대적인지 여부를 확인해야합니다. 상대가 아닌 경우 모든 것이 정상입니다. 만약 pwd
로 경로를 확인할 수 있다면 /
및 $BASH_SOURCE
.
이 솔루션은 tcsh가 아닌 bash에만 적용됩니다. 함수 내에서 경로를 찾으려면 일반적으로 제공되는 답변 ${BASH_SOURCE[0]}
이 작동하지 않습니다.
파일이 소스인지 스크립트로 실행되는지에 관계 없이이 줄이 항상 작동하는 것으로 나타났습니다.
echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}
심볼릭 링크를 따르려면 위의 경로에서 readlink
을 재귀 적 또는 비재 귀적으로 사용하십시오.
다음은 시도해보고 다른 제안 된 솔루션과 비교하는 스크립트입니다. source test1/test2/test_script.sh
또는 bash test1/test2/test_script.sh
로 호출하십시오.
#
# Location: test1/test2/test_script.sh
#
echo $0
echo $_
echo ${BASH_SOURCE}
echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}
cur_file="${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}"
cur_dir="$(dirname "${cur_file}")"
source "${cur_dir}/func_def.sh"
function test_within_func_inside {
echo ${BASH_SOURCE}
echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}
}
echo "Testing within function inside"
test_within_func_inside
echo "Testing within function outside"
test_within_func_outside
#
# Location: test1/test2/func_def.sh
#
function test_within_func_outside {
echo ${BASH_SOURCE}
echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}
}
원 라이너가 작동하는 이유는 BASH_SOURCE
환경 변수와 관련 FUNCNAME
를 사용하여 설명합니다.
BASH_SOURCE
멤버가 FUNCNAME 배열 변수에서 해당 쉘 함수 이름이 정의 된 소스 파일 이름 인 배열 변수입니다. 쉘 기능 $ {FUNCNAME [$ i]}은 $ {BASH_SOURCE [$ i]} 파일에 정의되어 있으며 $ {BASH_SOURCE [$ i + 1]}에서 호출되었습니다.
기능 명
현재 실행 호출 스택에있는 모든 셸 함수의 이름을 포함하는 배열 변수입니다. 인덱스가 0 인 요소는 현재 실행중인 셸 함수의 이름입니다. 맨 아래 요소 (인덱스가 가장 높은 요소)는 "main"입니다. 이 변수는 Shell 함수가 실행될 때만 존재합니다. FUNCNAME에 대한 지정은 적용되지 않으며 오류 상태를 리턴합니다. FUNCNAME을 설정하지 않으면 나중에 다시 설정하더라도 특수 특성이 손실됩니다.
이 변수는 BASH_LINENO 및 BASH_SOURCE와 함께 사용할 수 있습니다. FUNCNAME의 각 요소에는 호출 스택을 설명하기 위해 BASH_LINENO 및 BASH_SOURCE에 해당 요소가 있습니다. 예를 들어 $ {FUNCNAME [$ i]}은 (는) 행 번호 $ {BASH_LINENO [$ i]}의 $ {BASH_SOURCE [$ i + 1]} 파일에서 호출되었습니다. 발신자 기본 제공은이 정보를 사용하여 현재 호출 스택을 표시합니다.
[출처 : 배쉬 매뉴얼]
철저하고 검색을 위해 여기에있는 일이 있습니다 ... 커뮤니티 위키이므로 다른 Shell의 해당 항목을 자유롭게 추가하십시오 (분명히 $ BASH_SOURCE가 다름).
test.sh :
#! /bin/sh
called=$_
echo $called
echo $_
echo $0
echo $BASH_SOURCE
test2.sh :
#! /bin/sh
source ./test.sh
$./test2.sh
./test2.sh
./test2.sh
./test2.sh
./test.sh
$ sh ./test2.sh
/bin/sh
/bin/sh
./test2.sh
./test.sh
$./test2.sh
./test2.sh
./test2.sh
./test2.sh
$/bin/sh ./test2.sh
/bin/sh
/bin/sh
./test2.sh
$
$ ./test2.sh
./test.sh
./test.sh
./test.sh
$ zsh test.sh
echo
test.sh
$
이것은 bash, dash, ksh 및 zsh에서 나를 위해 일했습니다.
if test -n "$BASH" ; then script=$BASH_SOURCE
Elif test -n "$TMOUT"; then script=${.sh.file}
Elif test -n "$ZSH_NAME" ; then script=${(%):-%x}
Elif test ${0##*/} = dash; then x=$(lsof -p $$ -Fn0 | tail -1); script=${x#n}
else script=$0
fi
echo $script
이 쉘에 대한 출력 :
BASH source: ./myscript
ZSH source: ./myscript
KSH source: /home/pbrannan/git/theme/src/theme/web/myscript
DASH source: /home/pbrannan/git/theme/src/theme/web/myscript
BASH: ./myscript
ZSH: ./myscript
KSH: /home/pbrannan/git/theme/src/theme/web/myscript
DASH: ./myscript
Csh/tcsh에서 작동 시키려고했지만 너무 어렵습니다. POSIX를 고수하고 있습니다.
나는 커뮤니티 위키 답변 (숀 J. 고프의)에 약간 혼란 스러웠으므로 물건을 정리하는 스크립트를 작성했습니다. $_
에 대해, 나는 이것을 발견했다 : 명령에 전달 된 환경 변수로서 _
의 사용법 . 환경 변수이므로 값을 잘못 테스트하기 쉽습니다.
아래는 스크립트이며 출력입니다. 그들은 또한 이 요점 에 있습니다.
#!/bin/bash
# test-Shell-default-variables.sh
# Usage examples (you might want to `Sudo apt install zsh ksh`):
#
# ./test-Shell-default-variables.sh dash bash
# ./test-Shell-default-variables.sh dash bash zsh ksh
# ./test-Shell-default-variables.sh dash bash zsh ksh | less -R
# `-R` in `less -R` to have less pass escape sequences directly to the terminal
# so we have colors.
# The "invoking with name `sh`" tests are commented because for every Shell I
# tested (dash, bash, zsh and ksh), the output was the same as that of dash.
# The `test_expression` function also work with expansion changes. You can try
# lines like `test_expression '{BASH_SOURCE:-$0}'`.
echolor() {
echo -e "\e[1;[email protected]\e[0m"
}
tell_file() {
echo File \`"$1"\` is:
echo \`\`\`
cat "$1"
echo \`\`\`
echo
}
Shell_ARRAY=("[email protected]")
test_command() {
for Shell in "${Shell_ARRAY[@]}"
do
prepare "$Shell"
cmd="$(eval echo $1)"
# echo "cmd: $cmd"
printf '%-4s: ' "$Shell"
{ env -i $cmd 2>&1 1>&3 | sed 's/^/[err]/'; } 3>&1
teardown
done
echo
}
prepare () {
Shell="$1"
PATH="$PWD/$Shell/sh:$PATH"
}
teardown() {
PATH="${PATH#*:}"
}
###
### prepare
###
for Shell in "${Shell_ARRAY[@]}"
do
mkdir "$Shell"
ln -sT "/bin/$Shell" "$Shell/sh"
done
echo > printer.sh
echo '. ./printer.sh' > sourcer.sh
rm linked.sh &>/dev/null; ln -sT "printer.sh" "linked.sh"
tell_file sourcer.sh
###
### run
###
test_expression() {
local expr="$1"
# prepare
echo "echo $expr" > printer.sh
tell_file printer.sh
# run
cmd='$Shell ./printer.sh'
echolor "\`$cmd\` (simple invocation) ($expr):"
test_command "$cmd"
# cmd='sh ./printer.sh'
# echolor "\`$cmd\` (when executable name is \`sh\`) ($expr):"
# test_command "$cmd"
cmd='$Shell ./sourcer.sh'
echolor "\`$cmd\` (via sourcing) ($expr):"
test_command "$cmd"
# cmd='sh ./sourcer.sh'
# echolor "\`$cmd\` (via sourcing, when name is \`sh\`) ($expr):"
# test_command "$cmd"
cmd='$Shell ./linked.sh'
echolor "\`$cmd\` (via symlink) ($expr):"
test_command "$cmd"
# cmd='sh ./linked.sh'
# echolor "\`$cmd\` (via symlink, when name is \`sh\`) ($expr):"
# test_command "$cmd"
echolor "------------------------------------------"
echo
}
test_expression '$BASH_SOURCE'
test_expression '$0'
test_expression '$(/bin/true x y; true a b c; echo $_)' # Rq: true is a builtin
test_expression '$_'
###
### teardown
###
for Shell in "${Shell_ARRAY[@]}"
do
rm "$Shell/sh"
rm -d "$Shell"
done
rm sourcer.sh
rm linked.sh
rm printer.sh
./test-Shell-default-variables.sh {da,ba,z,k}sh
출력File `sourcer.sh` is:
```
. ./printer.sh
```
File `printer.sh` is:
```
echo $BASH_SOURCE
```
`$Shell ./printer.sh` (simple invocation) ($BASH_SOURCE):
dash:
bash: ./printer.sh
zsh :
ksh :
`$Shell ./sourcer.sh` (via sourcing) ($BASH_SOURCE):
dash:
bash: ./printer.sh
zsh :
ksh :
`$Shell ./linked.sh` (via symlink) ($BASH_SOURCE):
dash:
bash: ./linked.sh
zsh :
ksh :
------------------------------------------
File `printer.sh` is:
```
echo $0
```
`$Shell ./printer.sh` (simple invocation) ($0):
dash: ./printer.sh
bash: ./printer.sh
zsh : ./printer.sh
ksh : ./printer.sh
`$Shell ./sourcer.sh` (via sourcing) ($0):
dash: ./sourcer.sh
bash: ./sourcer.sh
zsh : ./printer.sh
ksh : ./sourcer.sh
`$Shell ./linked.sh` (via symlink) ($0):
dash: ./linked.sh
bash: ./linked.sh
zsh : ./linked.sh
ksh : ./linked.sh
------------------------------------------
File `printer.sh` is:
```
echo $(/bin/true x y; true a b c; echo $_)
```
`$Shell ./printer.sh` (simple invocation) ($(/bin/true x y; true a b c; echo $_)):
dash:
bash: c
zsh : c
ksh :
`$Shell ./sourcer.sh` (via sourcing) ($(/bin/true x y; true a b c; echo $_)):
dash:
bash: c
zsh : c
ksh :
`$Shell ./linked.sh` (via symlink) ($(/bin/true x y; true a b c; echo $_)):
dash:
bash: c
zsh : c
ksh :
------------------------------------------
File `printer.sh` is:
```
echo $_
```
`$Shell ./printer.sh` (simple invocation) ($_):
dash:
bash: bash
zsh :
ksh :
`$Shell ./sourcer.sh` (via sourcing) ($_):
dash:
bash: bash
zsh : ./printer.sh
ksh :
`$Shell ./linked.sh` (via symlink) ($_):
dash:
bash: bash
zsh :
ksh :
------------------------------------------
$BASH_SOURCE
$BASH_SOURCE
는 bash에서만 작동합니다.$0
와의 유일한 차이점은 현재 파일이 다른 파일에 의해 제공되었을 때입니다. 이 경우 $BASH_PROFILE
는 소스 파일 이름이 아닌 소스 파일 이름을 포함합니다.$0
$0
의 값은 bash의 $BASH_SOURCE
와 같습니다.$_
$_
는 대시와 ksh로 그대로 유지됩니다.$_
는 마지막 호출의 마지막 인수로 감소합니다.$_
를 "bash"로 초기화합니다.$_
를 그대로 둡니다. (소싱 할 때 "마지막 인수"규칙의 결과 일뿐입니다).sh
라는 심볼릭 링크를 통해 호출되면 해당 테스트와 관련하여 대시처럼 작동합니다.이 답변lsof
와 약간의 grep 마술이 tcsh에서 중첩 된 소스 파일에 대해 작업 할 가능성이있는 유일한 방법을 설명합니다.
/usr/sbin/lsof +p $$ | grep -oE /.\*source_me.tcsh
tl; dr script=$(readlink -e -- "${BASH_SOURCE}")
( bash 분명히)
$BASH_SOURCE
테스트 사례주어진 파일 /tmp/source1.sh
echo '$BASH_SOURCE '"(${BASH_SOURCE})"
echo 'readlink -e $BASH_SOURCE'\
"($(readlink -e -- "${BASH_SOURCE}"))"
다른 방식으로 파일 source
source
from /tmp
$> cd /tmp
$> source source1.sh
$BASH_SOURCE (source1.sh)
readlink -e $BASH_SOURCE (/tmp/source1.sh)
$> source ./source1.sh
$BASH_SOURCE (./source1.sh)
readlink -e $BASH_SOURCE (/tmp/source1.sh)
$> source /tmp/source1.sh
$BASH_SOURCE (/tmp/source1.sh)
readlink -e $BASH_SOURCE (/tmp/source1.sh)
source
from /
cd /
$> source /tmp/source1.sh
$0 (bash)
$BASH_SOURCE (/tmp/source1.sh)
readlink -e $BASH_SOURCE (/tmp/source1.sh)
source
/tmp/a
및 /var
$> cd /tmp/a
$> source ../source1.sh
$BASH_SOURCE (../source1.sh)
readlink -e $BASH_SOURCE (/tmp/source1.sh)
$> cd /var
$> source ../tmp/source1.sh
$BASH_SOURCE (../tmp/source1.sh)
readlink -e $BASH_SOURCE (/tmp/source1.sh)
$0
모든 경우에 스크립트에 추가 된 명령이있는 경우
echo '$0 '"(${0})"
source
스크립트는 항상 인쇄
$0 (bash)
그러나, 스크립트가 run 인 경우 (예 :.
$> bash /tmp/source1.sh
$0
은 문자열 값입니다. /tmp/source1.sh
.
$0 (/tmp/source1.sh)
$BASH_SOURCE (/tmp/source1.sh)
readlink -e $BASH_SOURCE (/tmp/source1.sh)
Bash Shell의 경우 @ Dennis Williamson의 답변 가장 도움이되었지만 Sudo
의 경우에는 작동하지 않았습니다. 이것은 :
if ( [[ $_ != $0 ]] && [[ $_ != $Shell ]] ); then
echo "I'm being sourced!"
exit 1
fi
If 문을 사용하지 않고 bash 및 zsh 호환 스크립트를 만들려면 간단히 ${BASH_SOURCE[0]:-${(%):-%x}}
을 작성하면됩니다. 결과 값은 정의 될 때 BASH_SOURCE[0]
에서, BASH_SOURCE [0]이 정의되지 않은 경우 ${(%):-%x}}
에서 가져옵니다.
가장 어려운 부분은 현재 소스 파일을 찾는 것이 우분투에서 sh 교체로 사용되는 대시 쉘입니다. 다음 코드 스 니펫은 소스에서 제공되는 스크립트에서 절대 경로를 판별하는 데 사용될 수 있습니다. bash에서 테스트 한 zsh 및 dash는 dash 및 sh로 호출되었습니다.
NB : 최신 realpath (1) 유틸리티 from GNU coreutils 패키지에 따라 다름
NB : lsof (1) 옵션도 확인해야합니다.이 페이지와 다른 페이지의 비슷한 조언이 Ubuntu 18 및 19에서 저에게 효과적이지 않았으므로 이것을 다시 만들어야했습니다.
getShellName() {
[ -n "$BASH" ] && echo ${BASH##/*/} && return
[ -n "$ZSH_NAME" ] && echo $ZSH_NAME && return
echo ${0##/*/}
}
getCurrentScript() {
local result
case "$(getShellName)" in
bash ) result=${BASH_SOURCE[0]}
;;
zsh ) emulate -L zsh
result=${funcfiletrace[1]%:*}
;;
dash | sh )
result=$(
lsof -p $$ -Fn \
| tail --lines=1 \
| xargs --max-args=2 \
| cut --delimiter=' ' --fields=2
)
result=${result#n}
;;
* ) result=$0
;;
esac
echo $(realpath $result)
}