Как определить, если сценарий находится в исходном состоянии


у меня есть сценарий, где я не хочу его называть exit Если это источник.

Я подумал проверить, если == bash но это имеет проблемы, если сценарий получен из другого сценария, или если пользователь создает его из другой оболочки, например ksh.

есть ли надежный способ обнаружения, если сценарий был произведен?

16 142

16 ответов:

это, кажется, переносится между Bash и Korn:

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

строка, похожая на эту или назначение типа 'pathname=" $_ " (с более поздним тестом и действием), должна быть в первой строке скрипта или в строке после shebang (которая, если используется, должна быть для ksh, чтобы она работала в большинстве случаев).

Если ваша версия 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]}" != "" ]] && echo "script ${BASH_SOURCE[0]} is being sourced ..."

после прочтения ответа @ DennisWilliamson, есть некоторые вопросы, см. ниже:

как этот вопрос стоять КШиБаш, в этом ответе есть еще одна часть, касающаяся КШ... увидеть ниже.

простой Баш путь

[ "" = "$BASH_SOURCE" ]

давайте попробуем (на лету, потому что это Баш мог ; -):

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

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

я использую source вместо . для читаемость (как . - это псевдоним для source):

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

обратите внимание, что номер процесса не меняется, пока процесс остается источников:

echo $$
29301

почему бы не использовать $_ == сравнение

для обеспечения многих случаев, я начинаю писать правда сценарий:

#!/bin/bash

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

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

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

сложнее: КШ сейчас...

как я не использую КШ большое, после некоторого чтения на 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 вблизи 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,ksh,zsh, включая cross-shell один, плюс a достаточно надежное POSIX-совместимое решение:

  • номера версий приведены те, на которых функциональность была проверен - скорее всего, эти решения работают и на гораздо более ранних версиях -обратная связь приветствуется.

  • используя POSIX особенности только (например,dash, который действует как /bin/sh на Ubuntu), есть нет надежная путь чтобы определить, является ли сценарий источником-см. Нижнюю часть этого ответа для лучшего приближение.

ниже приведено объяснение; версия Кросс-оболочки сложна, но она должна работать надежно:

  • Баш (проверено на 3.57)

    [[  != "$BASH_SOURCE" ]] && sourced=1 || sourced=0
    
  • КШ (проверено на 93u+)

    [[ $(cd "$(dirname -- "")" && 
       printf '%s' "${PWD%/}/")$(basename -- "") != "${.sh.file}" ]] &&
         sourced=1 || sourced=0
    
  • zsh (проверено на 5.0.5) - обязательно называть это вне функции

    [[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && sourced=1 || sourced=0
    
  • cross-shell (bash, ksh, zsh)

    ([[ -n $ZSH_EVAL_CONTEXT && $ZSH_EVAL_CONTEXT =~ :file$ ]] || 
     [[ -n $KSH_VERSION && $(cd "$(dirname -- "")" &&
        printf '%s' "${PWD%/}/")$(basename -- "") != "${.sh.file}" ]] || 
     [[ -n $BASH_VERSION &&  != "$BASH_SOURCE" ]]) && sourced=1 || sourced=0
    
  • кросс-оболочки, в том числе в POSIX-функций-только снаряды (dash,sh); не a шутка (один трубопровод) по техническим причинам и не полностью надежная (смотрите внизу):

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

объяснение:


Баш

[[ "" != "$BASH_SOURCE" ]] && sourced=1 || sourced=0
  • $BASH_SOURCE ... Всегда содержит файл скрипта аргумент, будь то источник или нет.
  • ...
    • если не получены: всегда идентичны $BASH_SOURCE
    • ЕСЛИ ИСТОЧНИК:
      • если источник из другого сценария: сценарий с который сценарий под рукой в настоящее время источников.
      • если источник в интерактивном режиме:
        • как правило, bash для оболочки без входа в систему и -bash для оболочки входа (например, на OSX), или, если Bash был вызван как sh, аналогично sh или -sh.
        • однако, если Bash был запущен (напрямую) с относительным или абсолютным путем этот путь будет отражен в .
        • также обратите внимание, что можно запустить Bash (или любую программу) с произвольным значением для , например, с помощью exec строение с .

КШ

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

специальная переменная ${.sh.file} несколько аналогично $BASH_SOURCE; обратите внимание, что ${.sh.file} вызывает a синтаксис в bash, zsh и dash, так что не забудьте выполнить его условно в сценариях с несколькими оболочками.

в отличие от Баша, и ${.sh.file} не гарантируется ровно идентично в случае без источника, как может быть относительные путь, в то время как ${.sh.file} всегда полное путь, так должен быть разрешен до полного пути, прежде чем сравнивающий.


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 только в деталях.

этой полагается на стандартное поведение , который zsh, например, совсем не выставку.

таким образом, самый безопасный подход к объедините надежные, специфичные для оболочки методы выше с резервное решение для всех остальных снарядов.

кончик шляпы к Stéphane Desneux и ответ для вдохновения (преобразование моего выражения оператора кросс-оболочки в 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 -- ) && pwd -P)/$(basename -- )" != "$(cd $(dirname -- ${.sh.file}) && pwd -P)/$(basename -- ${.sh.file})" ] && sourced=1
elif [ -n "$BASH_VERSION" ]; then
  [ "" != "$BASH_SOURCE" ] && sourced=1
else # All other shells: examine  for known shell binary filenames
  # Detects `sh` and `dash`; add additional shell filenames as needed.
  case ${0##*/} in sh|dash) sourced=1;; esac
fi

The 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 значения main и source различать контекст вызывающего абонента. Используя FUNCNAME[] сохраняет захват и разбор caller выход. Вы должны знать или рассчитать свою локальную глубину вызова, чтобы быть правильным, хотя. Такие случаи, как сценарий, получаемый из другой функции или сценария, приведут к тому, что массив (стек) будет глубже. (FUNCNAME - это специальная переменная массива bash, она должна иметь непрерывные индексы, соответствующие стеку вызовов, если она никогда не unset.)

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

(в bash-4.2 и более поздних вы можете использовать более простую форму ${FUNCNAME[-1]} вместо последнего элемента в массиве. Улучшено и упрощено благодаря комментарию Денниса Уильямсона ниже.)

однако, ваша проблема, как указано "у меня есть скрипт, где я не хочу, чтобы он называл "выход", если он находится в исходном состоянии". Общее bash идиома для этой ситуации:

return 2>/dev/null || exit

если скрипт находится в исходном состоянии, то return завершит исходный скрипт и вернется к абонент.

если скрипт выполняется, то return вернет ошибку (перенаправлено), и exit завершит работу скрипта как обычно. Оба return и exit может принять код выхода, если требуется.

к сожалению, это не работает в ksh (по крайней мере, не в производной версии AT&T у меня здесь), он лечит return что эквивалентно exit если вызывается вне функции или сценария с точечным источником.

Обновлено: что вы можете делать в современных версиях ksh это проверка специальной переменной .sh.level который установлен на глубину вызова функции. Для вызванного скрипта это будет изначально не задано, для сценария с точечным источником он будет установлен в 1.

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

issourced && echo this script is sourced

это не совсем так надежно, как версия bash, вы должны вызвать issourced() в файле, который вы тестируете с верхнего уровня или на известной глубине функции.

(вы также можете быть заинтересованы в этот код на github, который использует ksh функция дисциплины и некоторые отладочные ловушки обмана, чтобы эмулировать Баш FUNCNAME массив.)

канонический ответ здесь:http://mywiki.wooledge.org/BashFAQ/109 также предлагает $- как еще один индикатор (хотя и несовершенный) состояния оболочки.


Примечания:

  • можно создать функции bash с именами " main "и" source" (переопределение строение), эти имена могут появиться в FUNCNAME[] но пока тестируется только последний элемент в этом массиве, нет никакой двусмысленности.
  • у меня нет хорошего ответа pdksh. Самое близкое, что я могу найти, относится только к pdksh, где каждый источник сценария открывает новый файловый дескриптор (начиная с 10 для исходного сценария). Почти наверняка не то, на что вы хотите положиться...

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.

объяснение

The return оператор вызовет ошибку, если вы попытаетесь выполнить его вне функции или если сценарий не является источником. Попробуйте это из командной строки:

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, потому что внутри суб-оболочки возникла ошибка:

shell-prompt> echo $?
output: 1

Я дам конкретный ответ BASH. Korn-оболочку, к сожалению. Предположим, что имя вашего скрипта include2.sh; затем сделайте функцию внутри the include2.sh под названием am_I_sourced. Вот моя демо-версия include2.sh:

am_I_sourced()
{
  if [ "${FUNCNAME[1]}" = source ]; then
    if [ "" = -v ]; then
      echo "I am being sourced, this filename is ${BASH_SOURCE[0]} and my caller script/shell name was "
    fi
    return 0
  else
    if [ "" = -v ]; then
      echo "I am not being sourced, my script/shell name was "
    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, т. е. встроенные переменные FUNCNAME и BASH_SOURCE; увидеть их документация на странице руководства bash.

только два нюанса:

1) вызов am_I_calledдолжны пройдет на исходный скрипт, но не в любая функция, чтобы ${FUNCNAME[1]} возвращает что-то другое. Угу...вы могли бы проверить ${FUNCNAME[2]} -- но ты только усложняешь свою жизнь.

2) Функция am_I_calledдолжны находиться в исходном скрипте, если вы хотите узнать, каково имя файла быть включенным.

FWIW, прочитав все остальные ответы, я придумал следующее решение для меня:

это работает для всех сценариев, которые начнем с #!/bin/bash но также могут быть получены из разных оболочек.

#!/bin/bash

# Function definitions (API) and shell variables (constants) go here

main()
{
# The script's execution part goes here
}

unset BASH_SOURCE 2>/dev/null
test "." != ".$BASH_SOURCE" || main "$@"

этот рецепт сценария имеет следующие свойства:

  • если выполняется bash,main называется.
  • если найденный bash,main вызывается только в том случае, если вызывающий скрипт имеет такое же имя. (Например, если он сам является источником.)
  • если источником является оболочка, отличная от bash,main это не называется.
  • если выполняется оболочкой, отличной от bash,main это не называется.
  • если оценивать bash С eval (eval "`cat script`"все цитаты важны!) не приходит непосредственно из командной строки, это вызывает main. Для всех остальных вариантов eval,main не называемый.

  • если main не называется, он возвращает true ($?=0).

  • и это не зависит от недокументированного поведения, которое может измениться.

таким образом, за исключением некоторых маловероятных случаях, main вызывается только тогда, когда скрипт выполняется обычным способом. обычно это то, что вы хотите, тем более, что ему не хватает сложного трудного для понимания кода.

как BASH_SOURCE не может быть сбросить в bash, но во всех других оболочках это также ловит крайний случай, где BASH_SOURCE имеет значение .

обратите внимание, что он очень похож на Python-кода:

if __name__ == '__main__': main()

что также предотвращает вызов main, за исключением некоторых случаев, как вы можете импортировать/загрузить скрипт и применить __name__='__main__'

почему я думаю, что это хороший способ решить проблему

если у вас есть что-то, что может быть получено несколькими оболочками, должно быть совместимо. Однако (прочитайте другие ответы),поскольку нет портативного способа обнаружить sourceing, вы должны изменить правила.

путем принудительного выполнения скрипта /bin/bash, вы именно это и делаете.

этой решает все случаи, но после в этом случае скрипт не может работать напрямую:

  • /bin/bash не установлен или дисфункциональный (т. е. в загрузочной среде)
  • если вы передаете его в не-Баш-оболочку, как в curl https://example.com/script | $SHELL здесь $SHELL не bash

однако я не могу думать о какой-либо реальной причине, где вам это нужно, а также возможность источника точно такой же скрипт параллельно! Обычно вы можете обернуть его, так что сценарий всегда находится в исходном состоянии. Затем выполните main вручную. Как что:

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

важно:

последняя работает main в два раза, если $SHELL работает bash и строка не запускается из командной строки. (Но я действительно не могу придумать ни одной причины, по которой вы должны когда-либо использовать это в скрипте, за исключением намеренного обмана кода.)

Примечание

этот ответ был бы невозможен без помощи всех другие ответы! Даже неправильные - что заставило меня опубликовать это.

Я хотел бы предложить небольшую поправку к очень полезный ответ Денниса, чтобы сделать его немного более портативный, надеюсь:

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

, потому что [[ не признаются (несколько анальный цепкой ИМХО) в Debian POSIX совместимый shell,dash. Кроме того, могут потребоваться кавычки для защиты от имен файлов, содержащих пробелы, опять же в указанной оболочке.

это работает позже в сценарии и никак не зависит от переменной:

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

или

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

$_ довольно хрупкие. Вы должны проверить это, как первое, что вы делаете в сценарии. И даже тогда, это не гарантирует, чтобы содержать имя вашей оболочки (если источник) или имя сценария (если выполняется).

например, если пользователь установил BASH_ENV, затем в верхней части скрипта $_ содержит имя последней команды BASH_ENV сценарий.

лучший способ, который я нашел, это использовать как это:

name="myscript.sh"

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

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

к сожалению, этот способ не работает из коробки в zsh из-за functionargzero опция делает больше, чем предполагает ее название, и по умолчанию включена.

чтобы обойти это, я поставил unsetopt functionargzero в своем .zshenv.

я следовал mklement0 компактное выражение.

это аккуратно, но я заметил, что он может потерпеть неудачу в случае ksh при вызове следующим образом:

/bin/ksh -c ./myscript.sh

(он думает, что это источник, и это не потому, что он выполняет подоболочку) Но выражение будет работать, чтобы обнаружить это:

/bin/ksh ./myscript.sh

кроме того, даже если выражение компактно, синтаксис не совместим со всеми оболочками.

Так что я закончил со следующим кодом, который работает для Баш, ЗШ, тире и КШ

SOURCED=0
if [ -n "$ZSH_EVAL_CONTEXT" ]; then 
    [[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && SOURCED=1
elif [ -n "$KSH_VERSION" ]; then
    [[ "$(cd $(dirname -- ) && pwd -P)/$(basename -- )" != "$(cd $(dirname -- ${.sh.file}) && pwd -P)/$(basename -- ${.sh.file})" ]] && SOURCED=1
elif [ -n "$BASH_VERSION" ]; then
    [[  != "$BASH_SOURCE" ]] && SOURCED=1
elif grep -q dash /proc/$$/cmdline; then
    case  in *dash*) SOURCED=1 ;; esac
fi

не стесняйтесь добавлять поддержку экзотических оболочек:)

Я не думаю, что есть какой-либо портативный способ сделать это как в ksh, так и в bash. В bash вы можете обнаружить его с помощью caller выход, но я не думаю, что существует эквивалент в КШ.

Мне нужен был один лайнер, который работает на [mac, linux] с bash.версия >= 3 и ни один из этих ответов не соответствует счету.

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

сразу к делу: вы должны оценить, если переменная "$0" равно имени вашей оболочки.


Вот так:

#!/bin/bash

echo "First Parameter: "
echo
if [[ "" == "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% портативный способ обнаружения, если сценарий был получен или не.

по моему опыту (7 лет с Шеллскриптинг), единственный безопасный способ (не полагаясь на переменные среды с PIDs и так далее, что не безопасно из-за того, что это что-то переменная), вы должны:

  • расширьте возможности вашего if
  • используя переключатель / случай, если вы хотите.

оба варианта не могут быть автоматически масштабируется, но это безопаснее путь.



например:

когда вы создаете скрипт через сеанс SSH, значение, возвращаемое переменной "$0" (при использовании источник), составляет Баш.

#!/bin/bash

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

или

#!/bin/bash

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

Я закончил с проверкой [[ $_ == "$(type -p "")" ]]

if [[ $_ == "$(type -p "")" ]]; 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 "" показать полный путь Баш.