programing

스크립트의 소스 여부를 검출하는 방법

mbctv 2023. 4. 11. 22:31
반응형

스크립트의 소스 여부를 검출하는 방법

않았으면 있다.exit이치노

$0 == bash된 경우 .ksh

스크립트가 소스인지 아닌지를 검출하는 신뢰성 높은 방법이 있습니까?

크로스 셸을 포함한 , , , 및 비교적 견고한 POSIX 준거 솔루션:

  • 제공된 버전 번호는 기능이 검증된 버전 번호입니다(아마도 이 솔루션들은 훨씬 이전 버전에서도 작동할 입니다). 피드백을 환영합니다.

  • POSIX 기능만 사용(예:dash 역할을 /bin/shUbuntu)에서는 스크립트의 소스 여부를 확실하게 판단할 없습니다.최적의 개산은 이하를 참조해 주세요.

중요:

  • 솔루션은 스크립트발신자의해 송신되고 있는지 여부를 판별합니다.스크립트는 셸 자체 또는 다른 스크립트(그 자체가 송신원인지 아닌지가 있습니다).

    • 후자의 케이스도 검출하면 복잡해집니다.스크립트 다른 스크립트에서 송신되고 있는 경우를 검출할 필요가 없는 경우는, 다음의 비교적 심플한 POSIX 준거 솔루션을 사용할 수 있습니다.

       # Helper function
       is_sourced() {
         if [ -n "$ZSH_VERSION" ]; then 
             case $ZSH_EVAL_CONTEXT in *:file:*) return 0;; esac
         else  # Add additional POSIX-compatible shell names here, if needed.
             case ${0##*/} in dash|-dash|bash|-bash|ksh|-ksh|sh|-sh) return 0;; esac
         fi
         return 1  # NOT sourced.
       }
      
       # Sample call.
       is_sourced && sourced=1 || sourced=0
      
  • 아래의 모든 솔루션은 스크립트의 내부 기능이 아닌 최상위 범위에서 실행되어야 합니다.

아래는 한 줄의 설명입니다.크로스 셸 버전은 복잡하지만 견고하게 동작해야 합니다.

  • bash(3.57, 4.4.19 및 5.1.16으로 업데이트)
(return 0 2>/dev/null) && sourced=1 || sourced=0
  • ksh (93u+에서는 유효)
[[ "$(cd -- "$(dirname -- "$0")" && pwd -P)/$(basename -- "$0")" != "$(cd -- "$(dirname -- "${.sh.file}")" && pwd -P)/$(basename -- "${.sh.file}")" ]] && sourced=1 || sourced=0
  • zsh (5.0.5에서는 유효)
[[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && sourced=1 || sourced=0
  • 크로스 셸(cross-shell, ksh, zsh)
(
  [[ -n $ZSH_VERSION && $ZSH_EVAL_CONTEXT =~ :file$ ]] || 
  [[ -n $KSH_VERSION && "$(cd -- "$(dirname -- "$0")" && pwd -P)/$(basename -- "$0")" != "$(cd -- "$(dirname -- "${.sh.file}")" && pwd -P)/$(basename -- "${.sh.file}")" ]] || 
  [[ -n $BASH_VERSION ]] && (return 0 2>/dev/null)
) && sourced=1 || sourced=0
  • POSIX 준거. 기술상의 이유로 원라이너(단일 파이프라인)가 아니며 완전히 견고하지 않음(하단 참조):
sourced=0
if [ -n "$ZSH_VERSION" ]; 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|-sh|dash|-dash) sourced=1;; esac
fi

설명


때리다

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

주의: 이 기술은 사용자 5754163의 답변에서 수정되었습니다.원래 솔루션보다 견고하다는 것이 판명되었습니다.[[ $0 != "$BASH_SOURCE" ]] && sourced=1 || sourced=0 [1]

  • 가 Bash를 허용합니다.return스크립트의 최상위 범위에서는 스크립트가 소스된 경우에만 함수의 문만 사용할 수 있습니다.

    • ifreturn 스크립트의 최상위 범위에서 사용되며 오류 메시지가 출력되며 종료 코드가 로 설정됩니다.1.
  • (return 0 2>/dev/null)return서브셸로 에러 메시지를 억제합니다.이후 종료 코드는 스크립트가 발신되었는지 여부를 나타냅니다.0 ) ( ( ) " ( ) " ( ))1와합니다.&& ★★★★★★★★★★★★★★★★★」||하여 "Setting"을 설정합니다.sourced그에 따라 변동합니다.

    • . 서브셸을 실행해야 하기 입니다.왜냐하면 실행은return소스 스크립트의 최상위 범위에서 스크립트가 종료됩니다.
    • @Haozhun에게 경의를 표합니다.그는 명시적으로 를 사용하여 명령어를 강화했습니다.0return " of "; "per bash help: per bash helpreturn [N] ".": "N" "N" "N" "N" "N" "N" "N" "N" "N" "N" "N" "N" "N" "N" "N" "N" "N" "N" "N" "N" "N" "N" "N" "N" "N" "N" "N" "N" " 반환 상태가 반환 가 반환 상태가 반환 상태가 반환됩니다 버전은 " "N" "N" "N" "N" "N" "N" "N" "N"return사용자 셸의 마지막 명령어에 0이 아닌 반환값이 있는 경우, 오퍼랜드 없이]는 잘못된 결과를 생성합니다.

ksh

[[ "$(cd -- "$(dirname -- "$0")" && pwd -P)/$(basename -- "$0")" != "$(cd -- "$(dirname -- "${.sh.file}")" && pwd -P)/$(basename -- "${.sh.file}")" ]] && sourced=1 || sourced=0

변수 " " "${.sh.file} $BASH_SOURCE해 주세요.${.sh.file}bash, zsh 및 대시에 구문 오류가 발생하므로 여러 셸 스크립트에서 조건부로 실행해야 합니다.

「 」는 「 」입니다.$0 ★★★★★★★★★★★★★★★★★」${.sh.file}을 보증하지 않습니다. 경로 이름일 수 일 수 둘 다 입니다.다른 경우 상대 경로 또는 파일 이름 중 하나일 수도 있고 다른 하나는 완전한 경로일 수도 있습니다.라서서둘둘둘$0 ★★★★★★★★★★★★★★★★★」${.sh.file}비교하기 전에 전체 경로로 확인해야 합니다.전체 경로가 다를 경우 소싱이 암시됩니다.


zsh

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

$ZSH_EVAL_CONTEXT 컨텍스트에 대한 " " " " " " 。서브스트링file로하여.:는, 스크립트가 송신되고 있는 경우에만 존재합니다.

스크립트의 에서는, 「」를 참조해 주세요.$ZSH_EVAL_CONTEXT 으로 끝나다:file이 테스트의 대상이 됩니다. 「」가 있습니다.:shfunc.:file치환 중;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:cmdsubst가 추가됩니다.


POSIX 기능만 사용

특정 전제를 할 의향이 있는 경우 스크립트를 실행하는 셸의 바이너리 파일명알고 있으면 스크립트가 소스인지 아닌지에 대해 합리적으로 추측할 수 있습니다.
특히접근법은 스크립트가 다른 스크립트에 의해 소싱되는 경우를 검출하지 않음을 의미합니다.

이 답변의 "소스 호출 처리 방법" 섹션에서는 POSIX 기능으로 처리할 수 없는 엣지 케이스에 대해 자세히 설명합니다.

바이너리 파일명을 검사하려면 , 예를 들면, 의 표준 동작에 의존합니다.

따라서, 가장 안전한 접근법은 나머지 모든 셸에 대한 폴백 솔루션과 위의 견고한 고유의 방법을 결합하는 것이다.

요컨대:다음 솔루션:

  • 셸 고유의 테스트로 커버된 셸: 견고하게 동작합니다.

  • 기타 모든 셸에서: 스크립트가 다른 스크립트가 아닌 이러한 셸에서 직접 소스된 경우에만 작동합니다.

Stéphane Desneux에 대한 모자의 과 영감에 대한 그의 답변(내 크로스 셸 진술 표현을 변형)sh -호환성if을 이용하다

sourced=0
if [ -n "$ZSH_VERSION" ]; 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|-sh|dash|-dash) sourced=1;; esac
fi

하기 위해 각 파일명 「」등)이 것에 .sh)는 현재 상태로1회, 2회, 프리픽스가 부가되어 표시됩니다.-이는 사용자 정의 로그인 로 인터랙티브 셸이 실행되는 MacOS와 같은 환경을 고려하기 위한 것입니다.$0(셸에 ('패스리스'가 -. (한편)Thanks, t7e.sh ★★★★★★★★★★★★★★★★★」dash인터랙티브셸로 사용할 가능성은 낮습니다.목록에 추가할 필요가 있는 다른 셸도 있을 수 있습니다.


[1] 사용자 2689가 검출한 것은[[ $0 != "$BASH_SOURCE" ]]있는 스크립트를 실행하면, 그 파일명만을 에 건네주는 것으로, false positive 가 됩니다.bash "binary")bash my-script, 냐냐$0 그냥 '그냥'이 되는 거야.my-script에 , ,는.$BASH_SOURCE전체 경로입니다.일반적으로는 이 기술을 사용하여 에서 스크립트를 호출하지 않습니다.$PATH- 직접 호출하기만 하면 됩니다.my-script) - 조합하면 도움이 됩니다.-x디버깅에 사용합니다.

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와 Korn 사이에서 휴대할 수 있는 것 같습니다.

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

와 한 행 같은 pathname="$_"(나중에 테스트와 액션을 실시하는 경우)는 스크립트의 첫 번째 줄 또는 쉐방 뒤의 줄에 있어야 합니다(사용하는 경우 대부분의 상황에서 동작하기 위해서는 ksh를 위한 것이어야 합니다).

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

이 질문은 와 bash를 나타내므로 이 답변에는 에 관한 다른 부분이 있습니다.를 참조해 주세요.

한 배시 방식

[ "$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)

용 i i i i를 쓴다.source instead.위해서(「」로서).입니다.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

더 세게: 지금...

는 ksh를 잘 사용하지 않기 때문에 man 페이지에서 몇 가지 읽은 후 다음과 같은 시도를 합니다.

#!/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 is에 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[]、 [ - 3.0 ] 、 [ 1803 - 3.0 ] 、 [ ] 、 [ ] 、 [ (만( 1803 - 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")'

은 것은출 of of of of of of of of of 의 출력을 확인하는 것과 같습니다.caller값,main ★★★★★★★★★★★★★★★★★」source발신자의 콘텍스트를 구별합니다.「」를 사용합니다.FUNCNAME[]하면 캡처 및 할 수 .caller로컬 의 깊이를 합니다.이데올로기때문에FUNCNAME변수입니다.스택에 가 있습니다.단, "bash"가 "bash"가 아닌 한, "는 "bash"가 아닙니다.unset

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

(에서는 보다 형식(bash-4.2 이이 ( ( 、 ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ()을 할 수 ${FUNCNAME[-1]}대신 배열의 마지막 항목에 대해 입력합니다.Dennis Williamson씨입니다.

단, 여기서 말하는 문제는 "원하는 경우 '종료'라고 부르지 않는 스크립트가 있습니다."입니다.공통bash다음과 같습니다.

return 2>/dev/null || exit

는, 「」를 해 주세요.return는 소스 스크립트를 종료하고 발신자에게 돌아갑니다.

되고 있는 는, 「」를 해 주세요.return와 에러가 반환됩니다.exit을 사용하다 다.return ★★★★★★★★★★★★★★★★★」exit는 필요에 따라서 종료 코드를 취득할 수 있습니다.

쉽도 sadly sadly에서는 작동하지 ksh AT), (AT&T에서 파생된 버전에는 ), (AT&T에서 파생된 버전), (AT&T에서 파생된 버전), (AT&T에서 파생된 버전), (AT&T에서 파생된 버전), (AT&T에서 파생된 버전), (AT&T에서 파생된 버전), (AT&T에서 파생된 버전),return와와 to to 에 상당하는 exit함수 스크립트 또는 닷 분할 스크립트 외부에서 호출된 경우.

갱신일 :의 최신 버전에서 수행할 수 있는 작업ksh입니다..sh.level이 값은 함수 호출 깊이로 설정됩니다.호출된 스크립트의 경우 처음에는 설정 해제되며, 닷 소스 스크립트의 경우 1로 설정됩니다.

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

issourced && echo this script is sourced

만큼 강력하지 버전을 .issourced()최상위 수준 또는 알려진 기능 깊이에서 테스트 중인 파일에 저장해야 합니다.

(또한 이 코드를 사용하는 github에 관심이 있을 수 있습니다.ksh를 함수 및 FUNCNAME★★★★★★★★★★★★★★★★★★」

여기서의 정석적인 답변은 http://mywiki.wooledge.org/BashFAQ/109에서도 제공되고 있습니다.$-(불완전하지만) 셸 상태를 나타내는 또 다른 지표로 사용됩니다.


주의:

  • "main" 및 "source"라는 이름의 bash 함수를 만들 수 있습니다(빌트인 대신).이러한 이름은 다음 페이지에 표시될 수 있습니다.FUNCNAME[]다만, 그 어레이의 마지막 항목만 테스트되고 있는 한, 애매한 것은 없습니다.
  • 잘 요.pdksh가 찾을 수 가장 에만 pdksh스크립트의 소싱마다 새로운 파일 기술자가 열립니다(원래 스크립트의 경우 10부터 시작).당신이 의지하고 싶은 것은 거의 확실치 않지만...

편집자 메모:이 답변의 솔루션은 견고하게 기능하지만bash- - . - only. - 한정.
(return 2>/dev/null).

TL;DR

「」의 합니다.return발생합니다.스크립트가 원본이 아닌 경우 오류가 발생합니다.당신은 그 오류를 포착하고 필요에 따라 진행할 수 있습니다.

이걸 파일에 넣고 테스트라고 불러주세요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명령문은 함수 외부에서 실행하려고 하거나 스크립트가 소스되지 않은 경우 오류를 발생시킵니다.셸 에서 시험해 : 롬 、 음 、 음 、 음 、 try try 、

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

이 오류 메시지는 표시되지 않으므로 출력을 dev/null로 리다이렉트 할 수 있습니다.

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

이제 종료 코드를 확인합니다.0은 OK(오류가 발생하지 않음), 1은 오류가 발생했음을 나타냅니다.

shell-prompt> echo $?
output: 1

'하다'도 요.return서브셸 안에 있는 스테이트먼트.?return스테이트먼트는 그것을 실행하고, 음, 반환한다.서브셸에서 실행하면 스크립트에서 반환되지 않고 해당 서브셸에서 반환됩니다.을(를).$(...):

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

서브셸 내부에서 오류가 발생했기 때문에 서브셸의 종료 코드(1)가 표시됩니다.

shell-prompt> echo $?
output: 1

FWIW는 다른 모든 답변을 읽은 후 다음과 같은 해결책을 생각해냈습니다.

업데이트: 실제로 누군가가 다른 답변에서 수정된 오류를 발견하여 제 답변에도 영향을 미쳤습니다.이 업데이트도 개선되었다고 생각합니다(궁금하신 분은 편집 참조).

이것은 모든 스크립트에 대해 기능합니다.이 스크립트는 다른 셸에서 시작되지만 다른 셸에서 소스될 수 있습니다.또한 이 스크립트의 외부에 보관되어 있는 정보(설정 등)를 학습하기 위해서도 마찬가지입니다.main★★★★★★ 。

이 은 모든 경우에 것 .bash변종입니다.또한 시스템에는 해당되지 않습니다./bin/sh으로 하고 있습니다.bash. 하다.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 "$@"

두보다는 다음 두 줄의 해서 '읽을 수 ' 를 사용해서 '읽을 수 없다'는 설정을 안 됩니다.BASH_SOURCEset -emain:

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-script 것이어야 한다test sees sees sees 로 보다$BASH_SOURCE를 참조해 주세요.

  • 소스의 경우evalbash,main되지 않음호출되지 않음)BASH_SOURCE $0를 참조해 주세요.

  • main라고 하면 .bash 한 stdin에서 .$0 빈 거죠.( exec -a '' /bin/bash ) <script

  • 「 」에 평가되는 basheval )eval "`cat script`" 따옴표는 모두 중요!) 다른 스크립트 내에서 호출합니다.main.eval는 명령줄에서 직접 실행됩니다.stdin stdin din( ) 。BASH_SOURCE공백인 , 「」는 공백입니다.$0/bin/bash완전히 다른 것을 강요하지 않는 한)

  • ifmain됩니다.true )$?=0를 참조해 주세요.

  • 치 않은 동작에 되어 있지 만, 되어 있지 않습니다).unset 않다BASH_SOURCE( : : ) :

    • BASH_SOURCE 는 bash 예약 어레이입니다.단, 허용BASH_SOURCE=".$0"을 미치지 않을 bash를 참조해 주세요.
    • 라는 문서는 없습니다.BASH_SOURCE기능외에서 동작합니다.그러나 그 반대(기능에서만 작동한다는 것)는 문서화되지 않았습니다. 결과,가 되고 있습니다bash도 v4.3이 .bash하면 스크립트가.$BASH_SOURCE관찰된 대로 작동이 중지됩니다. 나는 하고 있다.BASH_SOURCE의 향후 버전을 위해 그대로 유지하다bash 나도 마찬가지야
    • find,는, 「(nice find, BTW!)」라고 합니다.( return 0 ) ①이 나옵니다0 및 「」의 1소스가 아닌 경우.저뿐만 아니라 POSIX는 이렇게 말하고 있습니다.return되지 않은및 from subshell로부터의 동작).return수 되고 있을 만, 의 AFAICS가 될 .bashaccidental 버전
  • 아쉽게도 실행되지 않습니다.(비교)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는 스크립트가 정상적으로 실행되었을 때만 호출됩니다.일반적으로 이것은 여러분이 원하는 것입니다. 특히 코드를 이해하기 어려운 복잡한 요소가 없기 때문입니다.

Python 코드와 매우 유사합니다.

if __name__ == '__main__': main()

, 「 」, 「 」, 「 」, 「 」, 「 」의 호출도 입니다.main, 일부 케이스를 Import/하고 Import/를 적용할 수 __name__='__main__'

이것이 과제를 해결하는 좋은 방법이라고 생각하는 이유

여러 셸에서 조달할 수 있는 것이 있으면 호환성이 있어야 합니다.단, (다른 답변을 읽어주세요)를 검출하는 (실장하기 쉬운) 휴대용 방법은 없기 때문에 규칙을 변경해야 합니다.

는 반드시 /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에서 발견된 새로운 발견으로 인해 편집되었습니다.

이것은 스크립트에서 나중에 작동하며 _ 변수에 의존하지 않습니다.

## Check to make sure it is not sourced:
Prog=myscript.sh
if [ $(basename $0) = $Prog ]; then
   exit 1  # not sourced
fi

또는

[ $(basename $0) = $Prog ] && exit

BASH 유 bash bash bash bash bash bash bash bash bash bash bash bash bash 。아, 아, 아, 아, 아, 아.이 「」라고 .include2.sh; 그 후, 내부로 함수를 만듭니다.include2.sh라고 하는am_I_sourcedinclude2.sh:

am_I_sourced()
{
  if [ "${FUNCNAME[1]}" = source ]; then
    if [ "$1" = -v ]; then
      echo "I am being sourced, this filename is ${BASH_SOURCE[0]} and my caller script/shell name was $0"
    fi
    return 0
  else
    if [ "$1" = -v ]; then
      echo "I am not being sourced, my script/shell name was $0"
    fi
    return 1
  fi
}

if am_I_sourced -v; then
  echo "Do something with sourced script"
else
  echo "Do something with executed script"
fi

이제 여러 가지 방법으로 실행해 보겠습니다.

~/toys/bash $ chmod a+x include2.sh

~/toys/bash $ ./include2.sh 
I am not being sourced, my script/shell name was ./include2.sh
Do something with executed script

~/toys/bash $ bash ./include2.sh 
I am not being sourced, my script/shell name was ./include2.sh
Do something with executed script

~/toys/bash $ . include2.sh
I am being sourced, this filename is include2.sh and my caller script/shell name was bash
Do something with sourced script

때문에 없이 하며, 쉬운 것을 .$_이BASH의 검사 「BASH」, 「BASH」)을 합니다.FUNCNAME ★★★★★★★★★★★★★★★★★」BASH_SOURCE page의 해 주세요.

단 두 가지 경고:

의 콜am_I_called 소스 스크립트로 실행할 필요가 있지만 어떤 기능 내에서도 실행할 수 없습니다.${FUNCNAME[1]}다른 것을 반환합니다.... ${FUNCNAME[2]}하지만 당신은 당신의 삶을 더 힘들게 만들 뿐입니다.

2) 기능am_I_called 포함할 파일의 이름을 확인하려면 소스 스크립트에 존재해야 합니다.

Dennis의 매우 도움이 되는 답변에 약간의 수정을 제안하고 싶습니다만, 조금 더 휴대하기 쉽게 하기 위해서입니다.

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

[[(일부 항문 리텐트 IMHO) Debian POSIX 호환 셸에서는 인식되지 않습니다.dash 스페이스를 경우가 있습니다에서는, 「」, 「」, 「」, 「」, 「」, 「」, 「」, 「」, 「」, 「」, 「」, 「」, 「」, 「」, 「」, 「」, 「」, 「」, 「」, 「」의 파일명」이 필요합니다.

Bash 스크립트가 실행 중인지 소스(가져오기)인지를 검출하는 가장 아름다운 방법

저는 이것이 가장 아름다운 방법이라고 생각합니다.

eRCaGuy_hello_world repo에 있는 내 파일에서:

#!/usr/bin/env bash

main() {
    echo "Running main."
    # Add your main function code here
}

if [ "${BASH_SOURCE[0]}" = "$0" ]; then
    # This script is being run.
    __name__="__main__"
else
    # This script is being sourced.
    __name__="__source__"
fi

# Only run `main` if this script is being **run**, NOT sourced (imported)
if [ "$__name__" = "__main__" ]; then
    echo "This script is being run."
    main
else
    echo "This script is being sourced."
fi

참고 자료:

  1. 실행 출력을 표시하는 등 위의 기술에 대한 자세한 내용은 다음 답변도 참조하십시오.Python과 동등한 bash는 무엇입니까?
  2. 은 처음 내용입니다."${BASH_SOURCE[0]}" = "$0"

원한다면 다음 대안을 검토할 수도 있지만, 저는 위의 코드 청크를 사용하는 것이 더 좋습니다.

중요:사용방법"${FUNCNAME[-1]}"어떤 스크립트가 다른 스크립트를 호출하거나 발신하는 경우, 테크놀로지는 네스트된 스크립트를 적절하게 처리하지 않습니다.if [ "${BASH_SOURCE[0]}" = "$0" ]기법은 그렇다.하나의 큰 이유이기도 합니다.if [ "${BASH_SOURCE[0]}" = "$0" ]★★★★★★ 。

bash 스크립트가 소스 또는 실행 중인지 여부를 판별하는 4가지

이것과 다른 몇 가지 질문들에 대한 답들을 여기저기서 읽었고, 요약해서 한 곳에 정리하고 싶은 4가지 방법들을 생각해냈다.

if __name__ == "__main__":

참조: Python에서 __name__ == "_main__:"의 경우 어떻게 해야 합니까?

  1. 아래 4가지 모든 기술에 대한 완전한 데모를 check_if_sourced_or_executed에서 볼 수 있습니다.sh 스크립트는 eRCaGuy_hello_world repo에 있습니다.
  2. 에서 사용 중인 기술 중 인수 해석, " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "main기능, 자동 실행 vs 소스 검출(예:__name__ == "__main__":Python의 경우 등)의 데모/템플릿 프로그램을 참조하십시오.현재는 라고 하고 있습니다만, 나중에 이름이 변경되면 바로 위 링크의 리스트에서 갱신합니다.

어쨌든, 4개의 Bash 테크닉을 다음에 나타냅니다.

  1. 기술 1(어디서나 배치 가능, 네스트된 스크립트 처리):참조: https://unix.stackexchange.com/questions/424492/how-to-define-a-shell-script-to-be-sourced-not-run/424495#424495

    if [ "${BASH_SOURCE[0]}" -ef "$0" ]; then
        echo "  This script is being EXECUTED."
        run="true"
    else
        echo "  This script is being SOURCED."
    fi
    
  2. 테크닉 2 [마음에 드는 기술](어디서나 배치할 수 있고, 네스트 스크립트를 취급할 수 있습니다)아래쪽에 있는 인수_parsing__3_advanced_gen_prog_template.sh에서 사용 중인 최신 bash 데모 스크립트를 참조하십시오.

    수정처:Python의 if __name__ == '_main__'에 해당하는 bash는 무엇입니까?

    if [ "${BASH_SOURCE[0]}" == "$0" ]; then
        echo "  This script is being EXECUTED."
        run="true"
    else
        echo "  This script is being SOURCED."
    fi
    
  3. 기법 3(모든 기능을 벗어나야 하는 다른 라인 필요):수정처:스크립트의 소스 여부를 검출하는 방법

    # A. Place this line OUTSIDE all functions:
    (return 0 2>/dev/null) && script_is_being_executed="false" || script_is_being_executed="true"
    
    # B. Place these lines anywhere
    if [ "$script_is_being_executed" == "true" ]; then
        echo "  This script is being EXECUTED."
        run="true"
    else
        echo "  This script is being SOURCED."
    fi
    
  4. 기술 4 [제한사항: 중첩된 스크립트를 처리하지 않음](함수 내에 있어야 함):수정처:스크립트의 소스 여부를 검출하는 방법
    Unix & Linux: 실행하지 않는 소스의 셸 스크립트를 정의하는 방법.

    if [ "${FUNCNAME[-1]}" == "main" ]; then
        echo "  This script is being EXECUTED."
        run="true"
    elif [ "${FUNCNAME[-1]}" == "source" ]; then
        echo "  This script is being SOURCED."
    else
        echo "  ERROR: THIS TECHNIQUE IS BROKEN"
    fi
    
    1. 은 '아까부터'입니다.${FUNCNAME[-1]}trick: @mr.synamic:대본이 소스인지 아닌지를 탐지하는 방법 - 그는 분명히 데니스 윌리엄슨으로부터 그것을 배웠다.

다음 항목도 참조하십시오.

  1. [내 답변] Python과 동등한 bash는 무엇입니까?
  2. [My Answer] Unix & Linux: 실행하지 않는 소스의 셸 스크립트를 정의하는 방법

$_경우 에서 가장 해야 하는 .대본에서 가장 먼저 해야 할 일로서 확인해야 합니다.또, 그 경우에도, 셸의 이름(소스 되어 있는 경우)이나 스크립트의 이름(실행되어 있는 경우)이 포함되어 있는 것은 보증되지 않습니다.

를 들어,가 ""를 설정한 ""는 ""를 의미합니다.BASH_ENV으로 스크립트의 「」를 참조해 주세요$_.BASH_ENV★★★★★★ 。

가장 은 가가찾 the the the the the the the the the the the the the the the the the 를 사용하는 것이다.$0음음음같 뭇매하다

name="myscript.sh"

main()
{
    echo "Script was executed, running main..."
}

case "$0" in *$name)
    main "$@"
    ;;
esac

유감스럽게도, 이 방법은 zsh에서는 즉시 사용할 수 없습니다.functionargzero옵션이 이름보다 더 많이 수행되고 기본적으로 켜져 있습니다.

이 문제를 해결하기 위해unsetopt functionargzero 집에서는.zshenv.

OP가 원하는 것은 아니지만, 그 기능을 로드하기 위해서(라이브러리로서) 스크립트를 소싱할 필요가 있는 경우가 많습니다.예를 들어 벤치마킹이나 테스트 목적으로 사용합니다.

POSIX를 포함한 모든 쉘에서 동작하는 설계를 다음에 나타냅니다.

  • 은 모두 1단계 합니다.run_main()★★★★★★ 。
  • 에서 첫 스크립트가 합니다.--no-run어떠한 동작도 수행하지 않는 인수 --no-run 라고 부를 수 .run_main.
  • source다음 중 하나:
set -- --no-run "$@"
. script.sh
shift

. ★★★★★★★★★★★★★★★★★」sourcePOSIX에 대한 무시합니다..전화를 "$@"일일

와 bashksh의 bash 모두 이할 수 에서는 bash를 사용하여 할 수 .caller"Ksh" "Ksh는 "Ksh" 입니다.

나는 mklement0의 콤팩트한 표현을 따라했다.

좋은 방법이지만 다음과 같이 호출하면 ksh의 경우 실패할 수 있다는 것을 알게 되었습니다.

/bin/ksh -c ./myscript.sh

(서브셸을 실행해서가 아니라 발신기지라고 생각됩니다) 단, 이 표현은 이것을 검출하기 위해서 기능합니다.

/bin/ksh ./myscript.sh

또한 식이 컴팩트하더라도 구문이 모든 셸과 호환되지 않습니다.

그래서 저는 bash,zsh,dash,ksh에 해당하는 다음 코드로 끝냈습니다.

SOURCED=0
if [ -n "$ZSH_EVAL_CONTEXT" ]; then 
    [[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && SOURCED=1
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
    [[ $0 != "$BASH_SOURCE" ]] && SOURCED=1
elif grep -q dash /proc/$$/cmdline; then
    case $0 in *dash*) SOURCED=1 ;; esac
fi

이색 쉘 서포트도 자유롭게 추가 가능 :)

이 문제에 대한 수정은 올바르게 동작하기 위해 이러한 사항을 인식할 필요가 있는 코드를 작성하지 않는 것입니다.그 방법은 코드를 함수에 넣는 것입니다.소스가 필요한 스크립트의 메인라인에 넣는 것이 아닙니다.

함수 내부의 코드는 다음과 같습니다.return 0 ★★★★★★★★★★★★★★★★★」return 1이렇게 하면 함수만 종료되므로 해당 제어는 함수를 호출한 모든 것으로 돌아갑니다.

이것은 함수를 소스 스크립트의 메인라인에서 호출하든 최상위 스크립트의 메인라인에서 호출하든 다른 함수에서 호출하든 상관없습니다.

소싱을 사용하여 함수 및 변수만 정의하고 다른 최상위 명령은 실제로 실행하지 않는 "라이브러리" 스크립트를 가져옵니다.

. path/to/lib.sh # defines libfunction
libfunction arg

그렇지 않으면 다음과 같습니다.

path/to/script.sh arg # call script as a child process

다음 중 하나가 아닙니다.

. path/to/script.sh arg  # shell programming anti-pattern

@mklement0 응답에 약간의 추가.소스인지 아닌지를 판별하기 위해 스크립트에서 사용한 커스텀 함수는 다음과 같습니다.

replace_shell(){
   if [ -n "$ZSH_EVAL_CONTEXT" ]; then 
      case $ZSH_EVAL_CONTEXT in *:file*) echo "Zsh is sourced";; esac
   else
      case ${0##*/} in sh|dash|bash) echo "Bash is sourced";; esac
   fi
}

""의 ""$ZSH_EVAL_CONTEXT"는 zsh입니다.toplevel:file:shfunc만 아니라toplevel:file,즉 '소싱 중', '소싱 중', '소싱 중'*:file*이 문제를 해결할 수 있습니다.

bash.version > = 3의 [mac, linux]에서 사용할 수 있는 한 줄의 라이너가 필요했는데, 이 답변들은 모두 적성에 맞지 않습니다.

[[ ${BASH_SOURCE[0]} = $0 ]] && main "$@"

바로 요점: 변수 "$0"이 셸 이름과 동일한지 여부를 평가해야 합니다.


다음과 같이 합니다.

#!/bin/bash

echo "First Parameter: $0"
echo
if [[ "$0" == "bash" ]] ; then
    echo "The script was sourced."
else
    echo "The script WAS NOT sourced."
fi


경유:

$ bash check_source.sh 
First Parameter: check_source.sh

The script WAS NOT sourced.

소스 경유:

$ source check_source.sh
First Parameter: bash

The script was sourced.



스크립트가 소스인지 아닌지를 100% 휴대용으로 검출하는 것은 매우 어렵습니다.

Shellscripting에서 7년간 일한 내 경험에 대해 유일하게 안전한 방법(PID를 가진 환경변수에 의존하지 않는 방법 등)은 다음과 같습니다.

  • 가능성을 넓히다
  • 스위치나 케이스를 사용해 주세요.

두 옵션 모두 자동 확장은 불가능하지만 더 안전한 방법입니다.



예를 들어 다음과 같습니다.

SSH 세션을 통해 스크립트를 소스할 경우 변수 "$0"에 의해 반환되는 값은 -bash입니다.

#!/bin/bash

echo "First Parameter: $0"
echo
if [[ "$0" == "bash" || "$0" == "-bash" ]] ; then
    echo "The script was sourced."
else
    echo "The script WAS NOT sourced."
fi

또는

#!/bin/bash

echo "First Parameter: $0"
echo
if [[ "$0" == "bash" ]] ; then
    echo "The script was sourced."
elif [[ "$0" == "-bash" ]] ; then
    echo "The script was sourced via SSH session."
else
    echo "The script WAS NOT sourced."
fi

하게 되었습니다.[[ $_ == "$(type -p "$0")" ]]

if [[ $_ == "$(type -p "$0")" ]]; then
    echo I am invoked from a sub shell
else
    echo I am invoked from a source command
fi

「」를 사용하는 curl ... | bash -s -- ARGS 스크립트를 온 더 플라이로 은 그냥 "0"이 .bash/bin/bash파일을 할 때 합니다.type -p "$0"풀합니다.

테스트:

curl -sSL https://github.com/jjqq2013/bash-scripts/raw/master/common/relpath | bash -s -- /a/b/c/d/e /a/b/CC/DD/EE

source <(curl -sSL https://github.com/jjqq2013/bash-scripts/raw/master/common/relpath)
relpath /a/b/c/d/e /a/b/CC/DD/EE

wget https://github.com/jjqq2013/bash-scripts/raw/master/common/relpath
chmod +x relpath
./relpath /a/b/c/d/e /a/b/CC/DD/EE

이것은, 「범용」크로스 셸 서포트에 관한 다른 회답과는 다른 것입니다.이는 특히 https://stackoverflow.com/a/2942183/3220983과 매우 유사하지만 약간 다릅니다.단점은 클라이언트스크립트가 (변수를 먼저 내보냄으로써) 사용방법을 존중해야 한다는 것입니다.강점은 이것이 간단하고 "어디서나" 작동해야 한다는 것입니다.컷 앤 페이스트의 즐거움을 위한 템플릿은 다음과 같습니다.

# NOTE: This script may be used as a standalone executable, or callable library.
# To source this script, add the following *prior* to including it:
# export ENTRY_POINT="$0"

main()
{
    echo "Running in direct executable context!"
}

if [ -z "${ENTRY_POINT}" ]; then main "$@"; fi

주의: 사용하고 있습니다.export이 메커니즘이 하위 프로세스로 확장될 수 있는지 확인하세요.

shebang 회선을 사용하여 대신 실행 중인지 확인합니다.

라인이 .#!/path/to/shell않으면 다른 그렇지 않으면 다른 크로스 셸 호환성 문제도 발생합니다.

따라서, 발신기지시에 동작하지 않는 커맨드를 시행하는 것으로써, 그것이 실행되고 있는지를 확인할 필요가 있습니다.

예: Bash 스크립트의 경우:

#!/usr/bin/env bash

if (return 0 2>/dev/null); then
    echo "Script was sourced."
fi

이 방법은 zsh에도 적용되며, sh는 셰방 라인을 변경하기만 하면 됩니다.

언급URL : https://stackoverflow.com/questions/2683279/how-to-detect-if-a-script-is-being-sourced

반응형