5

Может ли shell-скрипт устанавливать переменные окружения вызывающей оболочки?

21

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

Я знаю, что в csh/tcsh можно использовать команду:

setenv FOO foo

или, в случае sh/bash, выполнится следующая команда:

export FOO=foo

Однако обе эти команды устанавливают переменную только во время выполнения сценария.

Я уже в курсе, что использование команды:

source myscript

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

Но вот в чем загвоздка:

Я хочу, чтобы этот сценарий мог вызываться как из bash, так и из csh. Иными словами, я хочу, чтобы пользователи обеих оболочек могли запустить мой сценарий и изменить окружение своей оболочки. Таким образом, команда source для меня не подойдет, так как пользователь, работающий в csh, не сможет выполнить сценарий bash, и наоборот.

Существует ли разумное решение этой проблемы, которое не потребует написания и поддержки ДВУХ версий сценария?

5 ответ(ов)

6

Используйте синтаксис вызова "точка-пробел" для выполнения скрипта. Например, вот как это сделать, указывая полный путь к скрипту:

. /path/to/set_env_vars.sh

А вот как сделать то же самое, если вы находитесь в той же директории, что и скрипт:

. set_env_vars.sh

Такие команды выполняют скрипт в текущей оболочке, а не запускают новую (что произошло бы, если бы вы использовали ./set_env_vars.sh). Поскольку скрипт выполняется в той же оболочке, установленные вами переменные окружения будут доступны после выхода из скрипта.

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

3

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

0

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

Одно из решений — написать скрипт, который выводит правильные команды для tcsh или sh в зависимости от того, как он был вызван. Если ваш скрипт называется "setit", то выполните следующие команды:

ln -s setit setit-sh

и

ln -s setit setit-csh

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

eval `setit-sh`

или из csh:

eval `setit-csh`

Скрипт setit использует переменную $0, чтобы определить стиль вывода.

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

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

#!/bin/bash
arg0=$0
arg0=${arg0##*/}
for nv in \
   NAME1=VALUE1 \
   NAME2=VALUE2
do
   if [ x$arg0 = xsetit-sh ]; then
      echo 'export '$nv' ;'
   elif [ x$arg0 = xsetit-csh ]; then
      echo 'setenv '${nv%%=*}' '${nv##*=}' ;'
   fi
done

С учетом символических ссылок, перечисленных выше, и вызова с помощью обратных кавычек, всё работает как задумано.

Чтобы упростить вызов для csh, tcsh или подобных оболочек, вы можете создать алиас:

alias dosetit 'eval `setit-csh`'

А для sh, bash и подобных команд:

alias dosetit='eval `setit-sh`'

Приятная деталь заключается в том, что вам нужно поддерживать список лишь в одном месте. В теории, вы даже можете поместить этот список в файл и вставить cat nvpairfilename между "in" и "do".

Именно так раньше настраивались терминальные параметры для оболочки входа: скрипт выводил команды для выполнения в оболочке входа. Обычно использовался алиас для упрощения вызова, как, например, "tset vt100". Как было упомянуто в другом ответе, аналогичный функционал есть и в новостном сервере INN для Usenet.

0

Ваш код в .bash_profile определяет две функции для управления использованием прокси-сервера: noproxy и setproxy. Они работают как задумано, устанавливая и сбрасывая переменные окружения для прокси.

При вызове функции noproxy, она отключает прокси, вызывая /usr/local/sbin/noproxy, а затем очищает переменные http_proxy, HTTP_PROXY, https_proxy и HTTPS_PROXY, что соответствует вашим ожиданиям.

Аналогично, функция setproxy активирует прокси через sh /usr/local/sbin/proxyon и устанавливает переменные окружения http_proxy, HTTP_PROXY, https_proxy и HTTPS_PROXY на значение http://127.0.0.1:8118/, также как вы и хотели.

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

0

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

#!/bin/ksh
export TEREDO_WORMS=ukelele
exec $SHELL -i

Этот скрипт будет интерпретироваться оболочкой Korn, экспортирует переменную окружения, а затем заменяет сам себя новой интерактивной оболочкой.

Прежде чем запустить этот скрипт, переменная SHELL в окружении установлена в C shell, а переменная окружения TEREDO_WORMS не задана:

% env | grep SHELL
SHELL=/bin/csh
% env | grep TEREDO
%

Когда вы запускаете скрипт, вы переходите в новую оболочку, еще одну интерактивную C shell, но переменная окружения уже установлена:

% teredo
% env | grep TEREDO
TEREDO_WORMS=ukelele
%

Когда вы выходите из этой оболочки, управление возвращается к оригинальной оболочке:

% exit
% env | grep TEREDO
%

Переменная окружения не установлена в окружении исходной оболочки. Если вы используете exec teredo для запуска команды, то оригинальная интерактивная оболочка заменяется оболочкой Korn, которая устанавливает переменную окружения, а затем она в свою очередь заменяется новой интерактивной C shell:

% exec teredo
% env | grep TEREDO
TEREDO_WORMS=ukelele
%

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

Тот же механизм работает и для Bash, и для Korn shell. Вы можете заметить, что подсказка после команд выхода может появляться в странных местах.


Обратите внимание на обсуждение в комментариях. Это не решение, которое я бы рекомендовал, но оно выполняет заявленную цель — создание одного скрипта для установки окружения, который работает со всеми оболочками (принимающими опцию -i для создания интерактивной оболочки). Вы также могли бы добавить "$@" после опции, чтобы передать любые другие аргументы, что сделало бы оболочку пригодной как универсальный инструмент "установить окружение и выполнить команду". Вы можете захотеть убрать -i, если есть другие аргументы, что приведет к следующему:

#!/bin/ksh
export TEREDO_WORMS=ukelele
exec $SHELL "${@-'-i'}"

Часть "${@-'-i'}" означает: 'если список аргументов содержит хотя бы один аргумент, используйте оригинальный список аргументов; в противном случае замените отсутствующие аргументы на -i'.

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