Определение того, был ли выполнен сценарий оболочки "sourcing" его
Возможно ли для сценария оболочки проверить, был ли он выполнен через source
? То есть, например,
$ source myscript.sh
$ ./myscript.sh
Может ли myscript.sh отличаться от этих различных оболочек?
9 ответов
Я думаю, что Сэм хочет сделать, возможно, не возможно.
В какой степени возможен полуобжаренный обходной путь, зависит от...
- ... оболочка пользователей по умолчанию, и
- ... какие альтернативные оболочки им разрешено использовать.
Если я правильно понимаю требование Сэма, он хочет иметь "сценарий", myscript
, то есть...
- ... не исполняется напрямую, вызывая его по имени
myscript
(то естьchmod a-x
); - ... не косвенно исполняемый для пользователей путем вызова
sh myscript
или ссылаясьbash myscript
- ... запускать только содержащиеся в нем функции и команды, только если он вызван источником:
. myscript
Первое, что нужно учитывать, это
- Вызов скрипта напрямую по его имени (
myscript
) требуется первая строка в скрипте, как#!/bin/bash
или похожие. Это напрямую определит, какой установленный экземпляр исполняемого файла bash (или символическая ссылка) будет вызван для запуска содержимого скрипта. Это будет новый процесс оболочки. Требуется, чтобы в самом файле сценария был установлен исполняемый флаг. - Запуск сценария путем вызова двоичного файла оболочки с именем сценария (путь +) в качестве аргумента (
sh myscript
), то же самое, что и "1". - за исключением того, что флаг исполняемого файла устанавливать не нужно, и указанная первая строка с хеш-бенг также не требуется. Единственное, что нужно, это то, что вызывающему пользователю необходим доступ на чтение к файлу сценария. - Вызов сценария путем поиска его имени (
. 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
(также если получены внутри sh
), исполняющая оболочка увидит все эти $BASH_*
как пустые переменные среды. Опять же, это можно использовать внутри скрипта, чтобы обнаружить это условие и "сделать что-то"... но не помешать запуску следующих команд!
Теперь я предполагаю, что...
- ... скрипт использует
#!/bin/bash
как первая строка, - ... пользователи установили Bash в качестве своей оболочки и вызывают команды в следующей таблице из Bash, и это их оболочка входа в систему,
- ...
sh
доступен, и это символическая ссылка наbash
или жеdash
,
Это будет означать, что возможны следующие вызовы с перечисленными значениями для переменных среды
vars+invok's | ./scriptname | sh scriptname | bash scriptname |, имяскрипта ---------------+--------------+---------------+-----------------+------------- $0 | ./scriptname | ./scriptname | ./scriptname | -bash $SHLVL | 2 | 1 | 2 | 1 $SHELLOPTS | braceexpand: | (пусто) | braceexpand:.. | braceexpand: $BASH | /bin/bash | (пусто) | /bin/bash | / Бен / Баш $BASH_ARGV | (пусто) | (пусто) | (пусто) | имяскрипта $BASH_SUBSHELL | 0 | (пусто) | 0 | 0 $SHELL | /bin/bash | /bin/bash | /bin/bash | / Бен / Баш $OPTARG | (пусто) | (пусто) | (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
Это может или не может быть тем, что хотел запрашивающий, но в аналогичной ситуации я хотел, чтобы скрипт указывал, что он предназначен для получения, а не для непосредственного запуска.
Для достижения этого эффекта мой скрипт гласит:
#!/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 случаев.
Я пока не могу добавить комментарий (правила stackexchange), поэтому я добавляю свой собственный ответ:
Этот может работать независимо от того, делаем ли мы:
bash scriptname
scriptname
./scriptname
,
на обоих bash
а также mksh
,
if [ "${0##/*}" == scriptname ] # if the current name is our script
then
echo run
else
echo sourced
fi
Поскольку все наши машины имеют историю, я сделал это:
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: я думаю, что ответ Курта намного более полный, но я думаю, что это могло бы помочь.
Если у вас есть неизменяемый путь к файлу для обычных пользователей, то:
if [ "$(/bin/readlink -f "$0")" = "$KNOWN_PATH_OF_THIS_FILE" ]; then
# the file was executed
else
# the file was sourced
fi
(его также легко можно ослабить, чтобы проверить только имя файла или что-то еще).
Но ваши пользователи должны иметь разрешение на чтение, чтобы иметь возможность получить файл, поэтому абсолютно ничто не может помешать им делать то, что они хотят с файлом. Но это может помочь им не использовать это неправильно.
Это решение не зависит от Bashisms.
Судя по ответу Курта Пфайли, это работает для меня
if [ $SHLVL = 1 ]
then
echo 'script was sourced'
fi
Да, это возможно. В общем, вы можете сделать следующее:
#! /bin/bash
sourced () {
echo Sourced
}
executed () {
echo Executed
}
if [[ ${0##*/} == -* ]]; then
sourced
else
executed $@
fi
Дать следующий вывод:
$ ./myscript
Executed
$ . ./myscript
Sourced
В первом случае $0
будет "myscript.sh". Во втором случае это будет "./myscript". Но, в общем, нет никакого способа сказать, source
использовался.
Если вы расскажете нам о том, что вы пытаетесь сделать, а не о том, как вы хотите это сделать, возможно, будет получен лучший ответ.