[bash] 스크립트가 소스인지 감지하는 방법

나는 exit그것이 공급되고 있다면 그것을 호출하고 싶지 않은 스크립트를 가지고 있습니다 .

나는 확인하는 것을 생각했다 $0 == bash스크립트를 다른 스크립트에서 가져 왔거나 사용자가와 같은 다른 셸에서 소스를 가져 오는 경우 문제가 있는지 했습니다 ksh.

스크립트가 소스인지 감지하는 신뢰할 수있는 방법이 있습니까?



답변

이것은 Bash와 Korn 사이에서 이식 가능한 것으로 보입니다.

[[ $_ != $0 ]] && echo "Script is being sourced" || echo "Script is a subshell"

이것과 비슷한 줄이나 pathname="$_"(나중에 테스트와 액션이있는) 줄은 스크립트의 첫 줄이나 shebang 다음 줄에 있어야합니다. 대부분의 상황).


답변

Bash 버전이 BASH_SOURCE 배열 변수에 대해 알고 있다면 다음과 같이 시도하십시오.

# man bash | less -p BASH_SOURCE
#[[ ${BASH_VERSINFO[0]} -le 2 ]] && echo 'No BASH_SOURCE array variable' && exit 1

[[ "${BASH_SOURCE[0]}" != "${0}" ]] && echo "script ${BASH_SOURCE[0]} is being sourced ..."


답변

을위한 강력한 솔루션 bash, ksh,zsh 하는 포함 크로스 – 쉘 하나, 플러스 합리적으로 강력한 POSIX 호환 솔루션 :

  • 제공된 버전 번호는 기능이 검증 된 버전 번호입니다. 이러한 솔루션은 훨씬 이전 버전에서도 작동합니다 . 피드백도 환영 합니다.

  • POSIX 기능 만 사용하면 (예 : in dash, /bin/sh우분투에서 작동 ) 스크립트가 소스인지 여부를 확인할 수있는 강력한 방법없습니다 . 최상의 근사값 은 아래를 참조하십시오 .

원 라이너는 다음과 같습니다-아래 설명; 크로스 셸 버전은 복잡하지만 강력하게 작동해야합니다.

  • bash (3.57 및 4.4.19에서 확인)

    (return 0 2>/dev/null) && sourced=1 || sourced=0
  • ksh (93u +에서 확인)

    [[ $(cd "$(dirname -- "$0")" &&
       printf '%s' "${PWD%/}/")$(basename -- "$0") != "${.sh.file}" ]] &&
         sourced=1 || sourced=0
  • zsh (5.0.5에서 확인)- 함수 외부에서 이를 호출해야 합니다.

    [[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && sourced=1 || sourced=0
  • 크로스 쉘 (bash, ksh, zsh)

    ([[ -n $ZSH_EVAL_CONTEXT && $ZSH_EVAL_CONTEXT =~ :file$ ]] ||
     [[ -n $KSH_VERSION && $(cd "$(dirname -- "$0")" &&
        printf '%s' "${PWD%/}/")$(basename -- "$0") != "${.sh.file}" ]] ||
     [[ -n $BASH_VERSION ]] && (return 0 2>/dev/null)) && sourced=1 || sourced=0
  • POSIX 호환 ; 하지 않는 한 줄 기술적 인 이유와 대한 (단일 파이프 라인) 하지 완전히 강력한 (아래 참조)

    sourced=0
    if [ -n "$ZSH_EVAL_CONTEXT" ]; then
      case $ZSH_EVAL_CONTEXT in *:file) sourced=1;; esac
    elif [ -n "$KSH_VERSION" ]; then
      [ "$(cd $(dirname -- $0) && pwd -P)/$(basename -- $0)" != "$(cd $(dirname -- ${.sh.file}) && pwd -P)/$(basename -- ${.sh.file})" ] && sourced=1
    elif [ -n "$BASH_VERSION" ]; then
      (return 0 2>/dev/null) && sourced=1
    else # All other shells: examine $0 for known shell binary filenames
      # Detects `sh` and `dash`; add additional shell filenames as needed.
      case ${0##*/} in sh|dash) sourced=1;; esac
    fi

설명:


세게 때리다

(return 0 2>/dev/null) && sourced=1 || sourced=0

참고 :이 기술은 원래 솔루션보다 더 강력한 것으로 판명 되었으므로 user5754163의 답변 에서 채택되었습니다 . [[ $0 != "$BASH_SOURCE" ]] && sourced=1 || sourced=0[1]

  • Bash는 return스크립트 소스 가있는 경우에만 함수의 문장 및 스크립트의 최상위 범위에서 명령문을 허용 합니다 .

    • 소스return아닌 스크립트 의 최상위 범위에서 사용되는 경우 오류 메시지가 표시되고 종료 코드는로 설정됩니다 1.
  • (return 0 2>/dev/null)서브 쉘return 에서 실행 하고 오류 메시지를 억제합니다. 그 후 종료 코드는 스크립트가 소스인지 ( ) 아닌지 ( )를 나타내며 , 변수 는 변수 를 설정하기 위해 and 연산자 와 함께 사용 됩니다.01&&||sourced

    • return소스 스크립트의 최상위 범위에서 실행 하면 스크립트가 종료 되므로 서브 쉘을 사용해야 합니다.
    • 피연산자 로 명시 적으로 사용하여 명령을보다 강력하게 만든 @Haozhun 의 모자 팁 ; 그는 다음과 같이 지적했다 : bash의 도움 : “N이 생략되면, 리턴 상태는 마지막 명령의 상태이다.” 결과적으로, 피연산자없이 그냥 사용 된 이전 버전 은 사용자 쉘의 마지막 명령에 0이 아닌 리턴 값이있는 경우 잘못된 결과를 생성합니다.0returnreturn [N]return

ksh

[[ \
   $(cd "$(dirname -- "$0")" && printf '%s' "${PWD%/}/")$(basename -- "$0") != \
   "${.sh.file}" \
]] &&
sourced=1 || sourced=0

특수 변수 ${.sh.file}는 다소 유사합니다 $BASH_SOURCE. 참고 ${.sh.file}원인 구문 오류 의 bash, zsh을, 그리고 대시를 확신을 실행하는 조건부 멀티 쉘 스크립트.

bash는 달리, $0그리고 ${.sh.file}보장되지 않음 정확히 으로, 비 – 공급의 경우 동일 $0수 있습니다 상대 경로하면서, ${.sh.file}항상입니다 전체 경로 때문에, $0비교하기 전에 전체 경로로 해결해야합니다.


zsh

[[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && sourced=1 || sourced=0

$ZSH_EVAL_CONTEXT평가 컨텍스트에 대한 정보를 포함합니다. 함수 외부에서이를 호출하십시오. 전래 스크립트 [의 최고 수준 범위] 내부 $ZSH_EVAL_CONTEXT 으로:file .

경고 : 내부 명령 치환, zsh을 추가 :cmdsubst, 시험 때문에 $ZSH_EVAL_CONTEXT:file:cmdsubst$있다.


POSIX 기능 만 사용

특정 가정을 기꺼이 원한다면 , 스크립트를 실행할 수있는 쉘 의 이진 파일 이름아는 것에 근거하여 스크립트가 소스인지 여부에 대한 합리적이지만 확실한 증거는 아닙니다 .
특히 이는 스크립트가 다른 스크립트에 의해 제공되는 경우이 방법이 실패 함을 의미합니다
.

이 답변 에서 ” 원본 호출을 처리하는 방법”섹션에서는 POSIX 기능으로 만 처리 할 수없는 경우 에 대해 자세히 설명합니다.

이것은 의 표준 동작에 의존 $0, zsh인스턴스가 않습니다 들어 하지 나타낸다.

따라서 가장 안전한 방법은 위의 강력한 쉘 특정 방법을 나머지 모든 쉘에 대한 대체 솔루션 과 결합하는 것 입니다.

받는 모자의 팁 스테판 Desneux그의 대답 영감에 대한 (A로 내 크로스 – 쉘 문 식을 변환 sh호환 if문 및 기타 쉘에 대한 핸들러를 추가).

sourced=0
if [ -n "$ZSH_EVAL_CONTEXT" ]; then
  case $ZSH_EVAL_CONTEXT in *:file) sourced=1;; esac
elif [ -n "$KSH_VERSION" ]; then
  [ "$(cd $(dirname -- $0) && pwd -P)/$(basename -- $0)" != "$(cd $(dirname -- ${.sh.file}) && pwd -P)/$(basename -- ${.sh.file})" ] && sourced=1
elif [ -n "$BASH_VERSION" ]; then
  (return 0 2>/dev/null) && sourced=1
else # All other shells: examine $0 for known shell binary filenames
  # Detects `sh` and `dash`; add additional shell filenames as needed.
  case ${0##*/} in sh|dash) sourced=1;; esac
fi

[1] user1902689단순한 파일 이름 을 바이너리 에 전달 하여위치한[[ $0 != "$BASH_SOURCE" ]] 스크립트를 실행할 때 위양성을 나타내는 것을 발견했습니다 . 예를 들면, 때문에, 바로 다음이다 , 반면 은 IS 전체 경로 . 당신은 일반적으로 스크립트를 호출하기 위해이 기술을 사용하지 않을 동안 – 당신은 단지 그들을 호출 좋겠 직접 ( 이 -) 과 결합 될 때 도움 을위한 디버깅 .$PATHbashbash my-script$0my-script$BASH_SOURCE$PATHmy-script-x


답변

@DennisWilliamson의 답변을 읽은 후 몇 가지 문제가 있습니다. 아래를 참조하십시오.

이 질문은 이 답변에는 또 다른 부분이 있습니다. … 아래를 참조하십시오.

단순한 방법

[ "$0" = "$BASH_SOURCE" ]

bash가 😉 수 있기 때문에 즉시 시도해 봅시다.

source <(echo $'#!/bin/bash
           [ "$0" = "$BASH_SOURCE" ] && v=own || v=sourced;
           echo "process $$ is $v ($0, $BASH_SOURCE)" ')
process 29301 is sourced (bash, /dev/fd/63)

bash <(echo $'#!/bin/bash
           [ "$0" = "$BASH_SOURCE" ] && v=own || v=sourced;
           echo "process $$ is $v ($0, $BASH_SOURCE)" ')
process 16229 is own (/dev/fd/63, /dev/fd/63)

가독성을 위해 source대신 사용 합니다 .( .의 별칭과 같이 source).

. <(echo $'#!/bin/bash
           [ "$0" = "$BASH_SOURCE" ] && v=own || v=sourced;
           echo "process $$ is $v ($0, $BASH_SOURCE)" ')
process 29301 is sourced (bash, /dev/fd/63)

프로세스 소스가 유지 되는 동안 프로세스 번호는 변경되지 않습니다 .

echo $$
29301

$_ == $0비교 를 사용하지 않는 이유

많은 경우를 보장하기 위해 진정한 스크립트 를 작성하기 시작합니다 .

#!/bin/bash

# As $_ could be used only once, uncomment one of two following lines

#printf '_="%s", 0="%s" and BASH_SOURCE="%s"\n' "$_" "$0" "$BASH_SOURCE"
[[ "$_" != "$0" ]] && DW_PURPOSE=sourced || DW_PURPOSE=subshell

[ "$0" = "$BASH_SOURCE" ] && BASH_KIND_ENV=own || BASH_KIND_ENV=sourced;
echo "proc: $$[ppid:$PPID] is $BASH_KIND_ENV (DW purpose: $DW_PURPOSE)"

이것을 다음이라는 파일로 복사하십시오 testscript.

cat >testscript
chmod +x testscript

이제 테스트 할 수 있습니다 :

./testscript
proc: 25758[ppid:24890] is own (DW purpose: subshell)

괜찮아.

. ./testscript
proc: 24890[ppid:24885] is sourced (DW purpose: sourced)

source ./testscript
proc: 24890[ppid:24885] is sourced (DW purpose: sourced)

괜찮아.

그러나 -x플래그 를 추가하기 전에 스크립트를 테스트하려면 다음을 수행하십시오 .

bash ./testscript
proc: 25776[ppid:24890] is own (DW purpose: sourced)

또는 사전 정의 된 변수를 사용하려면

env PATH=/tmp/bintemp:$PATH ./testscript
proc: 25948[ppid:24890] is own (DW purpose: sourced)

env SOMETHING=PREDEFINED ./testscript
proc: 25972[ppid:24890] is own (DW purpose: sourced)

더 이상 작동하지 않습니다.

5 번째 줄에서 6 번째 줄로 주석을 옮기면 더 읽기 쉬운 대답이됩니다.

./testscript
_="./testscript", 0="./testscript" and BASH_SOURCE="./testscript"
proc: 26256[ppid:24890] is own

. testscript
_="_filedir", 0="bash" and BASH_SOURCE="testscript"
proc: 24890[ppid:24885] is sourced

source testscript
_="_filedir", 0="bash" and BASH_SOURCE="testscript"
proc: 24890[ppid:24885] is sourced

bash testscript
_="/bin/bash", 0="testscript" and BASH_SOURCE="testscript"
proc: 26317[ppid:24890] is own

env FILE=/dev/null ./testscript
_="/usr/bin/env", 0="./testscript" and BASH_SOURCE="./testscript"
proc: 26336[ppid:24890] is own

더 세게 : 지금…

내가 사용하지 않기 때문에 맨 페이지를 읽은 후에 많은 시도가 있습니다.

#!/bin/ksh

set >/tmp/ksh-$$.log

이것을 다음에 복사하십시오 testfile.ksh:

cat >testfile.ksh
chmod +x testfile.ksh

두 번 실행하는 것보다 :

./testfile.ksh
. ./testfile.ksh

ls -l /tmp/ksh-*.log
-rw-r--r-- 1 user user   2183 avr 11 13:48 /tmp/ksh-9725.log
-rw-r--r-- 1 user user   2140 avr 11 13:48 /tmp/ksh-9781.log

echo $$
9725

그리고 봐라:

diff /tmp/ksh-{9725,9781}.log | grep ^\> # OWN SUBSHELL:
> HISTCMD=0
> PPID=9725
> RANDOM=1626
> SECONDS=0.001
>   lineno=0
> SHLVL=3

diff /tmp/ksh-{9725,9781}.log | grep ^\< # SOURCED:
< COLUMNS=152
< HISTCMD=117
< LINES=47
< PPID=9163
< PS1='$ '
< RANDOM=29667
< SECONDS=23.652
<   level=1
<   lineno=1
< SHLVL=2

소스 실행에 유전 된 변수가 있지만 실제로는 관련이 없습니다 …

$SECONDS가까운 것을 확인할 수도 0.000있지만 수동 소스 사례 만 보장합니다 …

부모가 무엇인지 확인하려고 할 수도 있습니다 .

이것을 당신의 testfile.ksh:

ps $PPID

보다:

./testfile.ksh
  PID TTY      STAT   TIME COMMAND
32320 pts/4    Ss     0:00 -ksh

. ./testfile.ksh
  PID TTY      STAT   TIME COMMAND
32319 ?        S      0:00 sshd: user@pts/4

또는 ps ho cmd $PPID, 그러나 한 수준의 하위 세션에서만 작동합니다.

죄송합니다. 아래에서 신뢰할 수있는 방법을 찾지 못했습니다. .


답변

BASH_SOURCE[]대답 (나중에 bash는-3.0)는하지만, 간단한 것 BASH_SOURCE[]입니다 함수 본체 외부 작업에 문서화되어 있지 않습니다 (이것은 현재 man 페이지와 불일치에, 일에 발생).

Wirawan Purwanto가 제안한 가장 강력한 방법 FUNCNAME[1] 은 함수 내 에서 확인 하는 것입니다 .

function mycheck() { declare -p FUNCNAME; }
mycheck

그때:

$ bash sourcetest.sh
declare -a FUNCNAME='([0]="mycheck" [1]="main")'
$ . sourcetest.sh
declare -a FUNCNAME='([0]="mycheck" [1]="source")'

이 검사의 출력에 상당 caller값, mainsource발신자의 상황을 구별. 를 사용 FUNCNAME[]하면 caller출력 을 캡처하고 구문 분석 할 수 있습니다 . 그래도 로컬 통화 깊이를 알고 계산해야합니다. 스크립트가 다른 함수 나 스크립트 내에서 제공되는 경우 배열 (스택)이 더 깊어집니다. ( FUNCNAME특별한 bash 배열 변수이며 결코 그렇지 않은 한 호출 스택에 해당하는 연속 인덱스를 가져야합니다 unset.)

function issourced() {
    [[ ${FUNCNAME[@]: -1} == "source" ]]
}

(bash-4.2 이상 ${FUNCNAME[-1]}에서는 배열의 마지막 항목 대신 간단한 양식을 사용할 수 있습니다 . 아래 Dennis Williamson의 의견 덕분에 개선되고 단순화되었습니다.)

그러나 명시된대로 귀하의 문제는 ” 나는 그것이 소스 인 경우 ‘종료’를 호출하지 않는 스크립트가 있습니다 “입니다. bash이 상황에 대한 일반적인 관용구는 다음과 같습니다.

return 2>/dev/null || exit

스크립트가 소스 인 return경우 소스 스크립트를 종료하고 호출자에게 리턴합니다.

스크립트가 실행 중이면 return오류 (리디렉션 된)를 반환 exit하고 스크립트를 정상적으로 종료합니다. 모두 returnexit필요한 경우, 종료 코드를 취할 수 있습니다.

슬프게도, 이것은 ksh(적어도 내가 가지고있는 AT & T 파생 버전 에서는) 작동하지 않으며 함수 또는 도트 소스 스크립트 외부에서 호출 된 경우 return와 동일 하게 처리 됩니다 exit.

업데이트 : 최신 버전에서 할 수있는 일은 함수 호출 깊이로 설정된 ksh특수 변수를 확인하는 것 .sh.level입니다. 호출 된 스크립트의 경우 처음에 설정이 해제되고 도트 소스 스크립트의 경우 1로 설정됩니다.

function issourced {
    [[ ${.sh.level} -eq 2 ]]
}

issourced && echo this script is sourced

이것은 bash 버전만큼 강력하지는 않으므로 issourced()테스트중인 파일을 최상위 레벨이나 알려진 함수 깊이에서 호출해야합니다 .

(또한 bashhub 에서이 코드 에 관심 있을 수 있습니다. ksh 기능 은 규율 함수와 일부 디버그 트랩 트릭을 사용하여 bash FUNCNAME배열 을 에뮬레이트합니다 .)

여기 정식 답변 : http://mywiki.wooledge.org/BashFAQ/109$-쉘 상태에 대한 또 다른 지표 (불완전하지만)를 제공합니다.


노트:

  • “main”및 “source”( builtin을 재정의 함 ) 라는 bash 함수를 작성할 수 있습니다. 이러한 이름이 표시 될 수 FUNCNAME[]있지만 해당 배열의 마지막 항목 만 테스트되는 한 모호성이 없습니다.
  • 에 대한 좋은 답변이 없습니다 pdksh. 내가 찾을 수있는 가장 가까운 것은에 적용됩니다 pdksh. 스크립트의 각 소싱은 새 파일 설명자를 엽니 다 (원본 스크립트의 경우 10으로 시작). 거의 확실하게 당신이 의지하고 싶은 것이 아닙니다 …

답변

편집자 주 :이 답변의 솔루션은 강력하지만 효과가 bash있습니다. 로 간소화 할 수 있습니다
(return 2>/dev/null).

TL; DR

return명령문 을 실행하십시오 . 스크립트가 소스가 아닌 경우 오류가 발생합니다. 해당 오류를 파악하고 필요에 따라 진행할 수 있습니다.

이것을 파일에 넣고 test.sh라고 부릅니다.

#!/usr/bin/env sh

# Try to execute a `return` statement,
# but do it in a sub-shell and catch the results.
# If this script isn't sourced, that will raise an error.
$(return >/dev/null 2>&1)

# What exit code did that give?
if [ "$?" -eq "0" ]
then
    echo "This script is sourced."
else
    echo "This script is not sourced."
fi

직접 실행하십시오.

shell-prompt> sh test.sh
output: This script is not sourced.

그것을 소스 :

shell-prompt> source test.sh
output: This script is sourced.

나에게 이것은 zsh와 bash에서 작동합니다.

설명

return당신은 함수의 외부 또는 스크립트가 공급되지 않는 경우 그것을 실행하려고하면 문에서 오류가 발생합니다. 쉘 프롬프트에서 이것을 시도하십시오 :

shell-prompt> return
output: ...can only `return` from a function or sourced script

해당 오류 메시지를 볼 필요가 없으므로 출력을 dev / null로 리디렉션 할 수 있습니다.

shell-prompt> return >/dev/null 2>&1

이제 종료 코드를 확인하십시오. 0은 확인 (오류가 발생하지 않음)을 의미하고 1은 오류가 발생했음을 의미합니다.

shell-prompt> echo $?
output: 1

또한 return서브 쉘 내부 에서 명령문 을 실행하려고합니다 . 때 return문을 실행합니다. . . 잘. . . 보고. 하위 셸에서 실행하면 스크립트에서 반환되지 않고 해당 하위 셸에서 반환됩니다. 서브 쉘에서 실행하려면 다음과 같이 랩핑하십시오 $(...).

shell-prompt> $(return >/dev/null 2>$1)

이제 서브 쉘 내부에서 오류가 발생하여 서브 쉘의 종료 코드가 1이어야합니다.

shell-prompt> echo $?
output: 1


답변

FWIW, 다른 답변을 모두 읽은 후 다음 해결책을 찾았습니다.

업데이트 : 실제로 누군가가 내 대답 에 영향을 준 다른 답변 에서 수정 된 오류를 발견했습니다 . 여기 업데이트도 개선 된 것 같습니다 (호기심이 많은 경우 편집 참조).

이것은 모든 스크립트에서 작동 하지만 , 다른 쉘에서 시작될#!/bin/bash 수있을뿐만 아니라 main함수 외부에 유지되는 설정과 같은 일부 정보를 배우기 위해 제공 될 수도 있습니다 .

아래 의견에 따르면이 답변은 모든 bash변형에 대해 작동하지는 않습니다 . 또한에 /bin/sh기반한 시스템에는 해당되지 않습니다 bash. IE bashMacOS의 v3.x 에서는 실패합니다 . (현재 나는 이것을 해결하는 방법을 모른다.)

#!/bin/bash

# Function definitions (API) and shell variables (constants) go here
# (This is what might be interesting for other shells, too.)

# this main() function is only meant to be meaningful for bash
main()
{
# The script's execution part goes here
}

BASH_SOURCE=".$0" # cannot be changed in bash
test ".$0" != ".$BASH_SOURCE" || main "$@"

마지막 2 줄 대신 다음 BASH_SOURCE쉘을 사용 set -e하여 다른 쉘에 설정하지 않고 작업 할 수 있습니다 main.

if ( BASH_SOURCE=".$0" && exec test ".$0" != ".$BASH_SOURCE" ); then :; else main "$@"; fi

이 스크립트 레시피에는 다음과 같은 속성이 있습니다.

  • bash정상적인 방법으로 실행되면 main호출됩니다. bash -x script여기 script에는 경로가 포함되지 않은 통화는 포함되지 않습니다 ( 아래 참조).

  • 에 의해 공급하는 경우 bash, main호출 스크립트가 같은 이름을 가지고 일 경우에만 호출됩니다. (예를 들어 자체 소스 또는 bash -c 'someotherscript "$@"' main-script args..위치 를 통해main-scripttest 로 표시되는 경우 $BASH_SOURCE).

  • eval이외의 다른 셸 에서 소스 / 실행 / 읽기 / 처리 된 경우 bash,main 호출되지 않습니다 ( BASH_SOURCE항상 다릅니다 $0).

  • main 경우 호출되지 않습니다 bash$0빈 문자열로 설정하지 않는 한 stdin에서 스크립트를 읽는 .( exec -a '' /bin/bash ) <script

  • 다른 스크립트 내에서 bashwith eval ( eval "`cat script`" 모든 따옴표가 중요합니다! )로 평가되면을 호출합니다 main. 경우 eval직접 명령 줄에서 실행이 스크립트는 stdin에서 읽어 이전의 경우와 유사하다. ( BASH_SOURCE공백이지만 $0일반적으로 /bin/bash완전히 다른 것을 강요하지 않는 경우)

  • 경우 main호출되지 않습니다, 그것은 반환하지 않습니다 true($?=0 ).

  • 이것은 예기치 않은 동작에 의존하지 않습니다 (이전에는 문서화되지 않았지만 unset변경할 수없는 문서는 없습니다 BASH_SOURCE).

    • BASH_SOURCEbash 예약 배열 입니다. 그러나 허용BASH_SOURCE=".$0" 변경을 하면 매우 위험한 웜 캔이 열리므로 이것이 효과가 없어야한다고 기대합니다 (미래의 일부 버전에서는 추악한 경고가 표시되는 경우 제외 bash).
    • BASH_SOURCE외부 에서 작동하는 문서는 없습니다 . 그러나 그 반대 (기능에서만 작동하는)는 문서화되어 있지 않습니다. 관찰 결과, 작동하지만 ( bashv4.3 및 v4.4로 테스트 bash되었지만 더 이상 v3.x가 없습니다) 너무 많은 스크립트가 중단됩니다$BASH_SOURCE 관찰 된대로 작동 중지 됩니다. 따라서 내 기대는 BASH_SOURCE미래 버전의 그대로 유지됩니다.bash 에서도 .
    • 반면에 (좋은 발견은, BTW!) 고려 ( return 0 )주는, 0소스 경우 1공급하지 않을 경우. 이것은 조금 나를 위해뿐만 아니라 예상치 못한 제공 하고, POSIX는 것을 말한다 (이 수치에 따라) return서브 쉘이 정의되지 않은 동작입니다에서합니다 (그리고 return여기에 서브 쉘에서 명확하게). 아마도이 기능은 결국 더 이상 변경 될 수 없을 정도로 널리 사용 되기는하지만, AFAICS는 향후 bash버전에서 실수로 반환 동작이 변경 될 가능성이 훨씬 높습니다 .
  • 불행히도 bash -x script 1 2 3실행되지 않습니다 main. ( 경로가없는 script 1 2 3곳 비교 script). 해결 방법으로 다음을 사용할 수 있습니다.

    • bash -x "`which script`" 1 2 3
    • bash -xc '. script' "`which script`" 1 2 3
    • 그것은 bash script 1 2 3실행되지 않는 main기능이 고려 될 수있다.
  • 참고 것을 ( exec -a none script )호출 main( bash그것의 통과하지 않습니다$0 사용하기 위해 필요한이 들어, 스크립트에 -c마지막 점 참조).

따라서 일부 경우를 제외하고 main스크립트가 일반적인 방식으로 실행될 때만 호출됩니다. 일반적으로 이것은 원하는 것입니다. 특히 이해하기 어려운 코드가 없기 때문에 특히 그렇습니다.

파이썬 코드와 매우 유사합니다.

if __name__ == '__main__': main()

main스크립트를 가져오고로드하고 강제로 실행할 수 있으므로 일부 경우를 제외하고의 호출을 방지 합니다.__name__='__main__'

이것이 도전을 해결하는 좋은 일반적인 방법이라고 생각하는 이유

여러 쉘에서 제공 할 수있는 것이 있으면 호환 가능해야합니다. 그러나 (다른 답변을 읽으십시오), ing 을 감지하는 휴대용 방법이 없기 source때문에 규칙을 변경해야합니다 .

스크립트를 다음과 같이 실행해야합니다. /bin/bash 이를 정확하게 수행 할 수 있습니다.

이렇게 하면 모든 경우가 해결되지만 다음과 같은 경우에는 스크립트를 직접 실행할 수 없습니다.

  • /bin/bash 설치되어 있지 않거나 작동하지 않습니다 (즉, 부팅 환경에서)
  • 당신이 같은 쉘에 파이프하면 curl https://example.com/script | $SHELL
  • (참고 : bash최근에 사용한 경우에만 해당됩니다 .이 레시피는 특정 변형에 대해 실패한 것으로보고되었으므로 귀하의 사례에 적합한 지 확인하십시오.)

그러나 나는 당신이 그것을 필요로하는 실제 이유와 정확히 똑같은 스크립트를 병렬로 제공하는 능력에 대해 생각할 수 없습니다! 일반적 main으로 손으로 감쌀 수 있습니다 . 그렇게 :

  • $SHELL -c '. script && main'
  • { curl https://example.com/script && echo && echo main; } | $SHELL
  • $SHELL -c 'eval "`curl https://example.com/script`" && main'
  • echo 'eval "`curl https://example.com/script`" && main' | $SHELL

노트

  • 이 답변은 다른 모든 답변의 도움 없이는 불가능했을 것입니다! 잘못된 것조차도-처음에 이것을 게시하게했습니다.

  • 업데이트 : https://stackoverflow.com/a/28776166/490291에 있는 새로운 발견으로 인해 편집되었습니다.