Подтвердить что ты не робот

Что такое bash эквивалент Python `if __name__ == '__main __'`?

В Bash я бы хотел иметь возможность как создать сценарий, так и выполнить файл. Что такое Bash, эквивалентный Python, if __name__ == '__main__'?

Я не нашел легкодоступного вопроса/решения по этой теме в Stackoverflow (я подозреваю, что задаю вопрос таким образом, чтобы он не соответствовал существующему вопросу/ответу, но это наиболее очевидный способ выражения фразы вопрос из-за моего опыта работы с Python).


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

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

  • Как только вы обнаружите, что сценарий получен, что является лучшим способом не запускать сценарий (и избежать непреднамеренных побочных эффектов (кроме импорта интересующих функций), таких как добавление/удаление/изменение среды/переменных)
  • Как только вы обнаружите, что сценарий выполняется вместо источника, каков канонический способ реализации вашего сценария (поместить его в функцию? Или, может быть, просто поместить его после оператора if? Если вы поместите его после оператора if, он будет иметь влияет?
  • большинство поисковых запросов в Google, которые я нашел в Bash, не охватывают эту тему (сценарий bash, который может быть как получен, так и выполнен), каков канонический способ реализовать это? тема не освещена, потому что это не рекомендуется или плохо делать? есть ли ошибки?
4b9b3361

Ответ 1

Решение:

if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
    main "[email protected]"
fi

Я добавил этот ответ, потому что мне нужен ответ, написанный в стиле для имитации Python if __name__ == '__main__', но в Bash.

Относительно использования BASH_SOURCE vs $_. Я использую BASH_SOURCE, потому что он кажется более надежным, чем $_ (link1, link2).


Вот пример, который я тестировал/проверял с помощью двух скриптов Bash.

script1.sh с функцией xyz():

#!/bin/bash

xyz() {
    echo "Entering script1 xyz()"
}

main() {
    xyz
    echo "Entering script1 main()"
}

if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
    main "[email protected]"
fi

script2.sh, который пытается вызвать функцию xyz():

#!/bin/bash

source script1.sh

xyz    

Ответ 2

Нет. Обычно я использую это:

#!/bin/bash

main()
{
    # validate parameters

    echo "In main: [email protected]"

    # your code here
}

main "[email protected]"

Если вы хотите узнать, является ли этот script source 'd, просто заверните вызов main в

if [[ "$_" != "$0" ]]; then
    echo "Script is being sourced, not calling main()" 
else
    echo "Script is a subshell, calling main()"
    main "[email protected]"
fi

Ссылка: Как определить, есть ли script источник

Ответ 3

Использовать FUNCNAME

Способ 1: проверить FUNCNAME[0] (Bash 4. 3+)

#!/bin/bash

_main(){
    echo hello
}

if [[ ${FUNCNAME[0]} == "main" ]]; then
    _main
fi

Пример выполнения:

$ bash funcname_test.sh 
hello
$ source funcname_test.sh 
$ _main
hello

Я не уверен, как я наткнулся на это. man bash не очень хорошо описывает его функциональность, но вот что он на самом деле делает:

  • Выполненный скрипт: ${FUNCNAME[0]} является main
  • Источник сценария: ${FUNCNAME[0]} является source
  • Функция оболочки: ${FUNCNAME[0]} - это имя функции

Способ 2: проверить FUNCNAME[1] внутри функции (Bash 4.2)

В Bash 4.2 FUNCNAME доступно только внутри функции, где FUNCNAME[0] - это имя функции, а FUNCNAME[1] - имя вызывающей стороны. Таким образом, для функции верхнего уровня FUNCNAME[1] будет main в исполняемом скрипте или source в source скрипте.

Этот пример должен работать так же, как пример выше.

#!/bin/bash

get_funcname_1(){
    printf '%s\n' "${FUNCNAME[1]}"
}

_main(){
    echo hello
}

if [[ $(get_funcname_1) == main ]]; then
    _main
fi

Ответ 4

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

_main(){
    echo hello
}

# End sourced section
return 2> /dev/null

_main

Пример выполнения:

$ bash return_test.sh 
hello
$ source return_test.sh 
$ _main
hello

Если сценарий получен, return вернется к родителю (конечно), но если скрипт будет выполнен, return выдаст ошибку, и скрипт продолжит выполнение.

Я проверил это на GNU Bash 4.2 до 4.4

Это основано на части ответа mr.spuratic о том, как определить, что сценарий получен.

Предупреждение: это не работает в большинстве других оболочек.

Ответ 5

Я использовал следующую конструкцию в нижней части всех моих скриптов:

[[ "$(caller)" != "0 "* ]] || main "[email protected]"

Все остальное в script определено в функции или является глобальной переменной.

caller документируется как "Возврат контекста текущего вызова подпрограммы". При получении script результат вызывающего абонента начинается с номера строки script sourcing this. Если этот script не используется, он начинается с "0 "

Причина, по которой я использую != и || вместо = и &&, заключается в том, что последняя приведет к тому, что script вернет false при поиске. Это может привести к выходу внешнего script, если он работает под set -e.

Обратите внимание, что я знаю, что это работает с bash. Он не будет работать с оболочкой posix. Я не знаю о других оболочках, таких как ksh или zsh.