Определение того, был ли выполнен сценарий оболочки "sourcing" его - PullRequest
28 голосов
/ 08 сентября 2010

Возможно ли для сценария оболочки проверить, был ли он выполнен через source? То есть, например,

$ source myscript.sh
$ ./myscript.sh

Может ли myscript.sh отличаться от этих различных оболочек?

Ответы [ 9 ]

41 голосов
/ 14 сентября 2010

Я думаю, то, что Сэм хочет сделать, может быть невозможным.

В какой степени возможен полуобработанный обходной путь, зависит от ...

  • ... оболочки пользователей по умолчанию и
  • ...какие альтернативные оболочки им разрешено использовать.

Если я правильно понимаю требование Сэма, он хочет иметь «сценарий», myscript, то есть ...

  1. ... не непосредственно исполняемый через вызов его по имени myscript (то есть с chmod a-x);
  2. ... не косвенно исполняемыйдля пользователей, вызывая sh myscript или bash myscript
  3. ... только , запуская содержащиеся в нем функции и команды, если вызывается путем его получения: . myscript

Первое, что нужно учитывать, это

  1. Для вызова сценария напрямую по его имени (myscript) требуется первая строка в сценарии, такая как #!/bin/bash или аналогичная.Это напрямую определит, какой установленный экземпляр исполняемого файла bash (или символическая ссылка) будет вызван для запуска содержимого скрипта.Это будет новый процесс оболочки.Для этого требуется, чтобы в самом файле сценария был установлен флаг исполняемого файла.
  2. Запуск сценария путем вызова двоичного файла оболочки с именем сценария (путь +) в качестве аргумента (sh myscript), аналогично «1».- за исключением того, что не требуется устанавливать исполняемый флаг, и указанная первая строка с хеш-бэнгом тоже не требуется .Единственное, что нужно, это то, что вызывающему пользователю нужен read доступ к файлу скрипта.
  3. Вызов скрипта путем получения имени файла (. myscript) очень похож на «1».- за исключением того, что это не новая оболочка, которая вызывается.Все команды скрипта выполняются в текущей оболочке , используя его окружение (а также «загрязняя» свое окружение любыми (новыми) переменными, которые он может устанавливать или изменять. (Обычно это очень опасная вещь для выполнения: но здесь его можно использовать для выполнения exit $RETURNVALUE при определенных условиях ....)

Для '1.':
Легко достичь: chmod a-x myscript предотвратит непосредственное выполнение myscript, но не выполнит требования «2» и «3.».

для «2» и «3.»:
Гораздо сложнее достичь. Для вызовов sh myscript требуется чтение привилегий для файла. Так что очевидным выходом может показаться chmod a-r myscript. Однако, это также запретит '3'.: вы также не сможете получить исходный текст сценария.

Так как насчет написания сценария способом, который использует Bashism ? Bashism - это особый способ сделать что-то, что другие оболочкине понимаю: использование определенных переменных, команд и т. д. Это может быть использовано внутрискрипт для обнаружения этого условия и «что-то сделать» с ним (например, «display warning.txt», «mailto admin» и т. д.).Но ни в коем случае нельзя, чтобы это помешало sh или bash или любой другой оболочке прочитать и попытаться выполнить все следующие команды / строки, записанные в сценарии, если вы не уничтожите оболочку, вызвав exit.

Примеры: в Bash, среда, видимая сценарием, знает о $BASH, $BASH_ARGV, $BASH_COMMAND, $BASH_SUBSHELL, BASH_EXECUTION_STRING ....Если вызывается sh (также если sourced внутри sh), исполняющая оболочка увидит все эти $BASH_* как пустые переменные окружения.Опять же, это можно использовать внутри скрипта, чтобы обнаружить это условие и «сделать что-то» ... , но не помешать запуску следующих команд!

Я сейчас это предполагаю...

  1. ... сценарий использует #!/bin/bash в качестве первой строки,
  2. ... пользователи установили Bash в качестве оболочки и вызывают команды в следующей таблицеот Bash и это их оболочка для входа ,
  3. ... sh доступна и является символической ссылкой на bash или dash.

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

vars+invok's   | ./scriptname | sh scriptname | bash scriptname | . scriptname
---------------+--------------+---------------+-----------------+-------------
$0             | ./scriptname | ./scriptname  | ./scriptname    | -bash
$SHLVL         | 2            | 1             | 2               | 1
$SHELLOPTS     | braceexpand: | (empty)       | braceexpand:..  | braceexpand:
$BASH          | /bin/bash    | (empty)       | /bin/bash       | /bin/bash
$BASH_ARGV     | (empty)      | (empty)       | (empty)         | scriptname
$BASH_SUBSHELL | 0            | (empty)       | 0               | 0
$SHELL         | /bin/bash    | /bin/bash     | /bin/bash       | /bin/bash
$OPTARG        | (empty)      | (empty)       | (emtpy)         | (emtpy)

Теперь вы можете добавить логику в ваш текстовый скрипт:

  • Если $0 не равно -bash, тогда выполните exit $SOMERETURNVALUE.

Если сценарий был вызван через sh myscript или bash myscript, он будет выйти из вызывающей оболочки. Если он был запущен в текущей оболочке, он будет продолжать бежать. (Предупреждение: в случае, если скрипт имеет какие-либо другие операторы exit, ваша текущая оболочка будет «убита» ...)

Так что вставьте в свой неисполняемый файл myscript.txt в начале его что-то вроде это может сделать что-то близкое к вашей цели:

echo BASH=$BASH
test x${BASH} = x/bin/bash && echo "$? :    FINE.... You're using 'bash ...'"
test x${BASH} = x/bin/bash || echo "$? :    RATS !!! -- You're not using BASH and I will kick you out!"
test x${BASH} = x/bin/bash || exit 42
test x"${0}" = x"-bash"    && echo "$? :    FINE.... You've sourced me, and I'm your login shell."
test x"${0}" = x"-bash"    || echo "$? :    RATS !!! -- You've not sourced me (or I'm not your bash login shell) and I will kick you out!"
test x"${0}" = x"-bash"    || exit 33
15 голосов
/ 07 июля 2014

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

Для достижения этого эффекта мой скрипт гласит:

#!/bin/echo Should be run as: source
export SOMEPATH="/some/path/on/my/system"
echo "Your environment has been set up"

Поэтому, когда я запускаю его как команду или источник, я получаю:

$ ./myscript.sh
Should be run as: source ./myscript.sh

$ source ./myscript.sh
Your environment has been set up

Конечно, вы можете обмануть скрипт, запустив его как sh ./myscript.sh, но, по крайней мере, он дает правильное ожидаемое поведение в 2 из 3 случаев.

2 голосов
/ 28 февраля 2015

Это то, что я искал:

[[ ${BASH_SOURCE[0]} = $0 ]] && main "$@"
1 голос
/ 18 декабря 2016

Я пока не могу добавить комментарий (правила стек-обмена), поэтому я добавляю свой собственный ответ:

Этот может работать независимо от того, делаем ли мы:

  • bash scriptname
  • scriptname
  • ./scriptname.

на bash и mksh.

if [ "${0##/*}" == scriptname ]  # if the current name is our script
then
    echo run
else
    echo sourced
fi
1 голос
/ 17 июля 2015

Так как все наши машины имеют историю, я сделал это:

check_script_call=$(history |tail -1|grep myscript.sh )
if [ -z "$check_script_call" ];then
    echo "This file should be called as a source."
    echo "Please, try again this way:"
    echo "$ source /path/to/myscript.sh"
    exit 1
fi

Каждый раз, когда вы запускаете скрипт (без исходного кода), ваша оболочка создает новый env без истории.

Если вы хотите позаботиться о производительности, попробуйте следующее:

if ! history |tail -1|grep set_vars ;then
    echo -e "This file should be called as a source.\n"
    echo "Please, try again this way:"
    echo -e "$ source /path/to/set_vars\n"
    exit 1
fi

PS: я думаю, что ответ Курта гораздо более полный, но я думаю, что это могло бы помочь.

1 голос
/ 24 июля 2014

Да, это возможно. В общем, вы можете сделать следующее:

#! /bin/bash                                                                                       

sourced () {
    echo Sourced
}

executed () {
    echo Executed
}

if [[ ${0##*/} == -* ]]; then
    sourced
else
    executed $@
fi

Дает следующий вывод:

$ ./myscript
Executed
$ . ./myscript
Sourced
1 голос
/ 04 июля 2012

На основании ответа Курта Пфайля , это работает для меня

if [ $SHLVL = 1 ]
then
  echo 'script was sourced'      
fi

Пример

1 голос
/ 01 февраля 2012

Если у вас есть неизменяемый путь к файлу для обычных пользователей, то:

if [ "$(/bin/readlink -f "$0")" = "$KNOWN_PATH_OF_THIS_FILE" ]; then
    # the file was executed
else
    # the file was sourced
fi

(его также можно легко ослабить, чтобы проверить только имя файла или что-то еще).

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

Это решение не зависит от Bashisms.

0 голосов
/ 08 сентября 2010

В первом случае $0 будет "myscript.sh".Во втором случае это будет «./myscript».Но, в общем, нет никакого способа сказать, что source было использовано.

Если вы сообщите нам, что вы пытаетесь сделать, вместо того, как вы хотите это сделать, возможно, будет получен лучший ответ.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...