bash 셸 스크립트에서 실행과 소스를 구별합니까?


22

내가 여기에서 요구하는 것은 매우 정통 / 비 전통적 / 위험 중 하나이거나 Google fu 기술이 제대로 작동하지 않습니다 ...

A의 bash쉘 스크립트, 그것은 또 다른 쉘 스크립트에 의해 공급하게 될 경우 말하는 방식의 쉬운 존재하거나 자체에 의해 운영되고있다? 다시 말해, 다음 두 가지 행동을 구별 할 수 있습니까?

# from another shell script
source myScript.sh

# from command prompt, or another shell script
./myScript.sh

내가 생각하고있는 것은 bash소싱 할 때 사용할 수있는 기능을 포함하는 유틸리티와 같은 쉘 스크립트를 만드는 것 입니다. 이 스크립트가 단독으로 실행될 때는 정의 된 기능에 따라 특정 작업을 수행하기를 원합니다. 이 쉘 스크립트가 선택할 수있는 환경 변수가 있습니까? 예 :

some_function() {
    # ...
}
if [ -z "$IS_SOURCED" ]; then
    some_function;
fi

바람직하게는 플래그 변수를 설정하기 위해 호출자 스크립트가 필요없는 솔루션을 찾고 있습니다.

편집 : 스크립트를 소싱하고 실행하는 것의 차이점 , 사용중인 스크립트 의 차이를 구별 할 수 있는지 여기에서 찾으려고합니다 .



@ cuonglm은 내 질문을 편집했지만 둘 사이의 차이점을 알고 있지만 프로그래밍 방식으로 쉘 스크립트가 차이점을 말할 수 있는지 궁금합니다.
hjk

4
@cuonglm : 중복되지 않습니다. 그는 .명령에 대해 전혀 묻지 않고 스크립트가 소스 (source)인지 정상적으로 실행되는지 (즉, 서브 쉘에서) 감지하는 것에 관한 것입니다.
Jander

스택 오버 플로우에서 같은 질문에 아주 좋은 답변 : stackoverflow.com/a/28776166/96944
쟈니 Theunissen

답변:


19

예-$ 0 변수는 스크립트가 실행될 때 이름을 제공합니다.

$ cat example.sh
#!/bin/bash
script_name=$( basename ${0#-} ) #- needed if sourced no path
this_script=$( basename ${BASH_SOURCE} )
if [[ ${script_name} = ${this_script} ]] ; then
    echo "running me directly"
else
    echo "sourced from ${script_name}"
fi 

$ cat example2.sh
#!/bin/bash
. ./example.sh

다음과 같이 실행됩니다.

$ ./example.sh
running me directly
$ ./example2.sh
example.sh sourced from example2.sh

그것은 대화 형 쉘에서 소스가되는 것은 아니지만이 아이디어를 얻습니다 (바람직합니다).

BASH_SOURCE를 포함하도록 업데이트되었습니다-감사합니다 hjk


충분히 가까이 :) 최소한 스크립트 이름을 지정해야하는 약간의 장애가 있지만 다른 가능한 솔루션이없는 경우이 답변을 사용하겠습니다 ...
hjk

7

@DarkHeart의 답변을 환경 변수와 결합 BASH_SOURCE하면 트릭을 수행하는 것처럼 보입니다.

$ head example*.sh
==> example2.sh <==
#!/bin/bash
. ./example.sh

==> example.sh <==
#!/bin/bash
if [ "$(basename $0)" = "$(basename $BASH_SOURCE)" ]; then
    echo "running directly"
else
    echo "sourced from $0"
fi
$ ./example2.sh
sourced from ./example2.sh
$ ./example.sh
running directly

편집 난 그냥 요소의 수 계산한다면 여전히 간단한 해결책을 것 같다 BASH_SOURCE의 배열 :

if [ ${#BASH_SOURCE[@]} -eq 1 ]; then echo "running directly"; else echo "sourced from $0"; fi

1
'bash_source'변수를 동시에 찾은 것 같습니다. :)
DarkHeart

@DarkHeart 나는 배열 크기를 계산하는 사용법을 내 대답에 추가했습니다 ...이 경로에 대해 힌트를 주셔서 감사합니다! : D
hjk

1

방금 BusyBox와 같은 많은 종류의 라이브러리 스크립트를 만들었습니다. 그것에, 나는 다음 함수를 사용하여 그것이 공급되고 있는지 테스트합니다 ...

function isSourced () {
  [[ "${FUNCNAME[1]}" == "source" ]]  && return 0
  return 1
}

Bash가 유지 관리하는 FUNCNAME 배열은 기본적으로 함수 호출 스택입니다. $FUNCNAME(또는 ${FUNCNAME[0]})는 현재 실행중인 기능의 이름입니다. ${FUNCNAME[1]}그것을 호출 한 함수의 이름 등입니다.

최상위 항목은 스크립트 자체에 대한 특별한 값입니다. 그것은 포함됩니다 ...

  • 스크립트가 소스 인 경우 "source"라는 단어
  • 스크립트가 실행 중이고 테스트가 함수 내에서 수행되는 경우 "main"이라는 단어
  • ""(null) 스크립트가 실행 중이고 테스트가 함수 외부에서 수행되는 경우, 즉 스크립트 자체의 레벨에서 수행됩니다.

위의 함수는 실제로 스크립트 수준에서 호출 할 때만 작동합니다 (필요한 전부입니다). 배열 항목 번호가 잘못되어 다른 함수 내부에서 호출하면 실패합니다. 어디서나 작동하려면 스택의 최상위를 찾아 해당 값을 테스트해야합니다.

필요한 경우 "스택 상단"의 배열 항목 번호를 얻을 수 있습니다.

  local _top_of_stack=$(( ${#FUNCNAME[@]} - 1 ))

${#FUNCNAME[@]}배열의 항목 수입니다. 0부터 시작하는 배열로 1을 빼서 마지막 항목을 얻습니다.

이 세 함수는 파이썬과 유사한 함수 스택 추적을 생성하는 데 함께 사용 되며이 모든 기능이 어떻게 작동하는지 더 잘 알 수 있습니다 ...

function inspFnStack () {
  local T+="  "
  local _at=
  local _text="\n"
  local _top=$(inspFnStackTop)
  local _fn=${FUNCNAME[1]}; [[ $_fn =~ source|main ]]  || _fn+="()"
  local i=_top; ((--i))
  #
  _text+="$i item function call stack for $_fn ...\n"
  _text+="| L   BASH_SOURCE{BASH_LINENO called from}.FUNCNAME  \n"
  _text+="| ---------------------------------------------------\n"
  while (( $i > 0 ))
  do
    _text+="| $i ${T}$(inspFnStackItem $i)\n"
    T+="  "
    ((--i))
  done
  #
  printf "$_text\n"
  #
  return 0
}

function inspFnStackItem ()  {
  local _i=$1
  local _fn=${FUNCNAME[$_i]}; [[ $_fn =~ source|main ]]  || _fn+="()"
  local _at="${BASH_LINENO[$_i-1]}"; [[ $_at == 1 ]]  && _at="trap"
  local _item="${BASH_SOURCE[$_i]}{${_at}}.$_fn"
  #
  printf "%s" "$_item"
  return 0
}

function inspFnStackTop () {
  # top stack item is 1 less than length of FUNCNAME array stack
  printf "%d\n" $(( ${#FUNCNAME[@]} - 1 ))
  #
  return 0
}

FUNCNAME, BASH_SOURCE 및 BASH_LINENO는 마치 3 차원 배열 인 것처럼 bash에 의해 유지 관리되는 3 개의 배열입니다.


0

배열 계산이 신뢰할 수없는 것처럼 보이고 source점 ( .)을 사용하는 것이 매우 일반적이기 때문에 source키워드를 사용하기 때문에 사용 하지 않았다고 가정해야합니다 .

예를 들어, 다음을 포함하는 sourced.sh스크립트의 경우 echo $0:


$ . sourced.sh 
bash
$ source sourced.sh 
bash
$ chmod +x sourced.sh 
$ ./sourced.sh 
./sourced.sh
$ cat ./sourced.sh 
echo $0

비교 솔루션이 더 효과적이라고 제안했습니다.


0

대화식 쉘 에서 소싱 할 때도 작동하는 한 가지 방법 :

if [ $BASH_LINENO -ne 0 ]; then
    some_function;
fi

BASH_LINENO변수는 호출 함수에서 실행 된 모든 라인으로 배열된다. 스크립트를 직접 호출하거나 줄 번호에 해당하는 정수는 0이됩니다.

BASH_ * 변수 문서

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.