Дифференцирование между запуском и получением источника в сценарии оболочки bash?

Либо то, что я прошу здесь, это крайне неортодоксально / нетрадиционно / рискованно, или мои навыки Google-фу просто не в состоянии понюхать …

В сценарии оболочки 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 

Предпочтительно, я ищу решение, которое не требует, чтобы скрипт вызывающего абонента устанавливал любые переменные флага.

edit : Я знаю разницу между источником и запуском скрипта, что я пытаюсь выяснить здесь, если можно сказать разницу в используемом скрипте (в обоих направлениях).

Да – переменная $ 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

Объединение ответа @ 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 

edit Кажется, это было более простое решение, если бы я просто подсчитал количество элементов в BASH_SOURCE :

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

Я просто создал тот же скрипт библиотеки, который работает так же, как BusyBox. В нем я использую следующую функцию, чтобы проверить, если она используется …

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

Базируемый Bash массив FUNCNAME представляет собой стек вызовов функций. $FUNCNAME (или ${FUNCNAME[0]} ) – имя текущей исполняемой функции. ${FUNCNAME[1]} – имя вызываемой функции и т. Д.

Самый верхний элемент является особым значением для самого скрипта. Он будет содержать …

  • слово «источник», если источник сценария
  • слово «main», если скрипт выполняется, и тест выполняется изнутри функции
  • «" (null), если скрипт выполняется, а тест выполняется вне любой функции, то есть … на уровне самого скрипта.

Функция выше фактически работает только при вызове на уровне скрипта (который мне нужен). Это не получится, если вызвано из другой функции, потому что номер элемента массива будет неправильным. Чтобы заставить его работать в любом месте, нужно найти верхнюю часть стека и проверить это значение, что более сложно.

Если вам это нужно, вы можете получить номер элемента массива «вершины стека» с помощью …

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

${#FUNCNAME[@]} – количество элементов в массиве. В качестве массива с нулевым значением мы вычитаем 1 для получения последнего элемента #.

Эти три функции используются вместе для создания трассировки стека функций, аналогичных Python, и они могут дать вам представление о том, как все это работает …

 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, как если бы они были одним трехмерным массивом.