Может ли сценарий оболочки установить переменные среды вызывающей оболочки?
Я пытаюсь написать сценарий оболочки, который при запуске будет устанавливать некоторые переменные среды, которые будут оставаться установленными в оболочке вызывающего объекта.
setenv FOO foo
в csh/tcsh или
export FOO=foo
в sh / bash установите его только во время выполнения скрипта.
Я уже знаю, что
source myscript
будет запускать команды скрипта, а не запускать новую оболочку, и это может привести к настройке среды "вызывающего".
но вот руб.:
Я хочу, чтобы этот скрипт можно было вызвать из bash или csh. Другими словами, Я хочу, чтобы пользователи любой оболочки могли запускать мой скрипт и изменять среду своей оболочки. Так что "источник" не будет работать для меня, поскольку пользователь, работающий с хсг могут не источник bash-скрипт, и пользователь работает bash могут не источник сценарий хсг.
есть ли разумное решение, которое не включает в себя необходимость писать и поддерживать две версии сценария?
21 ответ:
ваш процесс оболочки имеет копию родительской среды и никакого доступа к среде родительского процесса вообще. Когда процесс оболочки завершается, все изменения, внесенные в его среду, теряются. Поиск файла сценария является наиболее часто используемым методом для настройки оболочки, вы можете просто хочу, чтобы стиснуть зубы и поддерживать для каждого из двух вариантах корпуса.
используйте синтаксис вызова "dot space script". Например, вот как это сделать, используя полный путь к скрипту:
. /path/to/set_env_vars.sh
и вот как это сделать, если вы находитесь в том же каталоге, что и скрипт:
. set_env_vars.sh
они выполняют скрипт под текущей оболочкой вместо загрузки другого (что и произошло бы, если бы вы сделали
./set_env_vars.sh
). Поскольку он работает в той же оболочке, установленные переменные среды будут доступны при его выходе.это то же самое как вызов
source set_env_vars.sh
, но он короче и может работать в некоторых местах, гдеsource
нет.
вы не сможете изменить оболочку вызывающего объекта, потому что она находится в другом контексте процесса. Когда дочерние процессы наследуют переменные оболочки, они наследование самих копий.
одна вещь вы можете сделать, это написать скрипт, который выдает правильные команды для tcsh или sh на основе того, как он вызывается. Если у вас скрипт "setit" то сделайте:
ln -s setit setit-sh
и
ln -s setit setit-csh
теперь либо непосредственно, либо в псевдониме, вы делаете это из ш
eval `setit-sh`
или это из csh
eval `setit-csh`
setit использует $0 для определения стиля вывода.
это напоминает о том, как люди используют, чтобы получить набор переменных среды термин.
преимущество здесь в том, что setit просто пишется в любой оболочке, которую вы любите, как в:
#!/bin/bash arg0= 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".это в значительной степени то, как настройки терминала оболочки входа в систему использовались: скрипт выводил бы статменты, которые будут выполняться в оболочке входа. Псевдоним, как правило, используется, чтобы сделать вызов просто, как в "цет вт100". Как упоминалось в другом ответе, существует также аналогичная функциональность в Inn UseNet news server.
в моем .файл у меня есть :
# No Proxy function noproxy { /usr/local/sbin/noproxy #turn off proxy server unset http_proxy HTTP_PROXY https_proxy HTTPs_PROXY } # Proxy function setproxy { sh /usr/local/sbin/proxyon #turn on proxy server http_proxy=http://127.0.0.1:8118/ HTTP_PROXY=$http_proxy https_proxy=$http_proxy HTTPS_PROXY=$https_proxy export http_proxy https_proxy HTTP_PROXY HTTPS_PROXY }
Так когда я хочу отключить прокси, функция(ы) запускается в оболочке входа и устанавливает переменные как и ожидалось и хотелось.
это" своего рода " возможно с помощью gdb и setenv(3), хотя мне трудно рекомендовать на самом деле делать это. (Кроме того, т. е. самый последний ubuntu на самом деле не позволит вам сделать это, не сказав ядру быть более терпимым к ptrace, и то же самое может произойти и для других дистрибутивов).
$ cat setfoo #! /bin/bash gdb /proc/${PPID}/exe ${PPID} <<END >/dev/null call setenv("foo", "bar", 0) END $ echo $foo $ ./setfoo $ echo $foo bar
это работает - это не то, что я бы использовал, но это "работает". Давайте создадим скрипт
teredo
для установки переменной окруженияTEREDO_WORMS
:#!/bin/ksh export TEREDO_WORMS=ukelele exec $SHELL -i
он будет интерпретирован оболочкой Korn, экспортирует переменную среды, а затем заменяет себя новой интерактивной оболочкой.
перед запуском этого скрипта, у нас есть
SHELL
установите в среде оболочку C и переменную окруженияTEREDO_WORMS
не установлено:% env | grep SHELL SHELL=/bin/csh % env | grep TEREDO %
когда скрипт запустите, вы находитесь в новой оболочке, другой интерактивной оболочке C, но переменная окружения установлена:
% teredo % env | grep TEREDO TEREDO_WORMS=ukelele %
когда вы выходите из этой оболочки, исходная оболочка берет на себя:
% exit % env | grep TEREDO %
переменная окружения не задана в среде исходной оболочки. Если вы используете
exec teredo
чтобы выполнить команду, затем исходная интерактивная оболочка заменяется оболочкой Korn, которая задает среду, а затем, в свою очередь, заменяется новым интерактивным C оболочка:% exec teredo % env | grep TEREDO TEREDO_WORMS=ukelele %
если ты типа
exit
(или Control-D), затем ваша оболочка выходит, вероятно, вы выходите из этого окна или возвращаетесь на предыдущий уровень оболочки, с которого начались эксперименты.тот же механизм работает для оболочки Bash или Korn. Вы можете обнаружить, что приглашение после команд выхода появляется в забавных местах.
обратите внимание на дискуссию в комментариях. Это не решение, которое я бы рекомендовал, но он достигает заявленной цели одного скрипта для установки среды, которая работает со всеми оболочками (которые принимают
-i
возможность сделать интерактивную оболочку). Вы также можете добавить"$@"
после опции ретрансляции любых других аргументов, которые затем могут сделать оболочку полезной в качестве общего инструмента "установить среду и выполнить команду". Вы, возможно, захотите, чтобы пропустить-i
если есть другие аргументы, ведущие к:#!/bin/ksh export TEREDO_WORMS=ukelele exec $SHELL "${@-'-i'}"
The
"${@-'-i'}"
бит означает 'если аргумент список содержит хотя бы один аргумент, используйте исходный список аргументов; в противном случае замените-i
для несуществующих аргументов.
вы должны использовать модули, см. http://modules.sourceforge.net/
EDIT: пакет модулей не обновлялся с 2012 года, но по-прежнему работает нормально для основ. Все новые функции, колокола и свистки происходят в lmod в этот день (что мне нравится больше):https://www.tacc.utexas.edu/research-development/tacc-projects/lmod
еще один обходной путь, который я не вижу упоминания, - это запись значения переменной в файл.
я столкнулся с очень похожей проблемой, когда я хотел иметь возможность запустить последний тест набора (вместо всех моих тестов). Мой первый план состоял в том, чтобы написать одну команду для установки переменной env TESTCASE, а затем иметь другую команду, которая будет использовать это для запуска теста. Излишне говорить, что у меня была такая же проблема, как и у вас.
но потом я придумал этот простой Хак:
первая команда (
testset
):#!/bin/bash if [ $# -eq 1 ] then echo > ~/.TESTCASE echo "TESTCASE has been set to: " else echo "Come again?" fi
вторая команда (
testrun
):#!/bin/bash TESTCASE=$(cat ~/.TESTCASE) drush test-run $TESTCASE
добавьте флаг-l в верхней части вашего скрипта bash, т. е.
#!/usr/bin/env bash -l ... export NAME1="VALUE1" export NAME2="VALUE2"
значения
NAME1
иNAME2
теперь будут экспортированы в текущую среду, однако эти изменения не являются постоянными. Если вы хотите, чтобы они были постоянными, вам нужно добавить их в свой.bashrc
файл или другой init-файл.из man-страницы:
-l Make bash act as if it had been invoked as a login shell (see INVOCATION below).
вы можете поручить дочернему процессу распечатать свои переменные среды (вызвав "env"), затем выполнить цикл над печатными переменными среды в Родительском процессе и вызвать "экспорт" для этих переменных.
следующий код основан на захват вывода найти . - print0 в массив bash
если родительская оболочка является bash, вы можете использовать
while IFS= read -r -d $'' line; do export "$line" done < <(bash -s <<< 'export VARNAME=something; env -0') echo $VARNAME
если родительская оболочка является тире, то
read
не предоставляет флаг-d и код становится более сложнымTMPDIR=$(mktemp -d) mkfifo $TMPDIR/fifo (bash -s << "EOF" export VARNAME=something while IFS= read -r -d $'' line; do echo $(printf '%q' "$line") done < <(env -0) EOF ) > $TMPDIR/fifo & while read -r line; do export "$(eval echo $line)"; done < $TMPDIR/fifo rm -r $TMPDIR echo $VARNAME
вы можете вызвать еще один Bash с другим bash_profile. Кроме того, вы можете создать специальный bash_profile для использования в среде multi-bashprofile.
помните, что вы можете использовать функции внутри bashprofile, и что функции будут доступны в глобальном масштабе. например, "function user { export USER_NAME $1 }" может установить переменную во время выполнения, например: user olegchir & & env | grep olegchir
технически это правильно - только "eval" не разветвляет другую оболочку. Однако с точки зрения приложения, которое вы пытаетесь запустить в измененной среде, разница равна нулю: дочерний элемент наследует среду своего родителя, поэтому (измененная) среда передается всем нисходящим процессам.
Ipso facto, измененная переменная среды 'прилипает' -- до тех пор, пока вы работаете под родительской программой/оболочкой.
Если это абсолютно необходимо, чтобы переменная окружения оставалась после выхода родительского элемента (Perl или оболочки), необходимо, чтобы родительская оболочка выполняла тяжелую работу. Один из методов, который я видел в документации, заключается в том, что текущий скрипт создает исполняемый файл с необходимым языком "экспорта", а затем обманывает родительскую оболочку в его выполнении-всегда осознавая тот факт, что вам нужно предварить команду "source", если вы пытаетесь оставить энергонезависимую версию измененной версии. окружающая среда позади. В лучшем случае Клюг.
второй метод заключается в изменении скрипта, который инициирует среду оболочки (.bashrc или что-то еще), чтобы содержать измененный параметр. Это может быть опасно - если вы используете сценарий инициализации, он может сделать вашу оболочку недоступной при следующей попытке запуска. Существует множество инструментов для изменения текущей оболочки; прикрепляя необходимые настройки к "пусковой установке", вы эффективно продвигаете эти изменения вперед, как что ж. Как правило, это не очень хорошая идея; если вам нужны только изменения среды для конкретного пакета приложений, вам придется вернуться и вернуть сценарий запуска оболочки в его первоначальное состояние (используя vi или что-то еще) после этого.
короче говоря, нет хороших (и простых) методов. Предположительно это было сделано с трудом, чтобы обеспечить безопасность системы не была безвозвратно скомпрометирована.
короткий ответ-Нет, вы не можете изменить среду родительского процесса, но похоже, что вы хотите создать среду с пользовательскими переменными среды и оболочкой, которую выбрал пользователь.
Так почему бы просто не что-то вроде
#!/usr/bin/env bash FOO=foo $SHELL
затем, когда вы закончите с окружающей средой, просто
exit
.
другой вариант-использовать "модули среды" (http://modules.sourceforge.net/). это, к сожалению, вводит третий язык в микс. Вы определяете среду с помощью языка Tcl, но есть несколько удобных команд для типичных модификаций (prepend vs.append vs set). Вам также потребуется установить модули среды. Затем вы можете использовать
module load *XXX*
чтобы назвать среду, которую вы хотите. Команда модуля-это в основном причудливый псевдоним дляeval
описанный выше механизм Томас Каммайер. Главным преимуществом здесь является то, что вы можете поддерживать среду на одном языке и полагаться на "модули среды", чтобы перевести ее на sh, ksh, bash, csh, tcsh, zsh, python (?!?!!), прием.
Я сделал это много лет назад. Если я правильно помню, я включил псевдоним в каждый из них .bashrc и и .cshrc, с параметрами, сглаживание соответствующих форм установки среды в общую форму.
тогда сценарий, который вы будете создавать в любой из двух оболочек, имеет команду с этой последней формой, которая подходит для псевдонимов в каждой оболочке.
Если я найду конкретные псевдонимы, я опубликую их.
Я создал решение, используя трубы, eval и сигнал.
parent() { if [ -z "$G_EVAL_FD" ]; then die 1 "Rode primeiro parent_setup no processo pai" fi if [ $(ppid) = "$$" ]; then "$@" else kill -SIGUSR1 $$ echo "$@">&$G_EVAL_FD fi } parent_setup() { G_EVAL_FD=99 tempfile=$(mktemp -u) mkfifo "$tempfile" eval "exec $G_EVAL_FD<>'$tempfile'" rm -f "$tempfile" trap "read CMD <&$G_EVAL_FD; eval \"$CMD\"" USR1 } parent_setup #on parent shell context ( A=1 ); echo $A # prints nothing ( parent A=1 ); echo $A # prints 1
Он может работать с любой командой.
в OS X bash вы можете сделать следующее:
Создайте файл сценария bash, чтобы отменить установку переменной#!/bin/bash unset http_proxy
сделать файл исполняемым
sudo chmod 744 unsetvar
создать псевдоним
alias unsetvar='source /your/path/to/the/script/unsetvar'
Он должен быть готов к использованию до тех пор, пока у вас есть папка, содержащая файл сценария, добавленный к пути.
Я не вижу никакого ответа, документирующего, как обойти эту проблему с взаимодействующими процессами. Общий шаблон с такими вещами, как
ssh-agent
иметь дочерний процесс печати выражение, которое родитель можетeval
.bash$ eval $(shh-agent)
например,
ssh-agent
имеет опции для выбора Csh или Bourne-совместимый синтаксис вывода.bash$ ssh-agent SSH2_AUTH_SOCK=/tmp/ssh-era/ssh2-10690-agent; export SSH2_AUTH_SOCK; SSH2_AGENT_PID=10691; export SSH2_AGENT_PID; echo Agent pid 10691;
(это приводит к запуску агента, но не позволяет вам фактически использовать его, если вы не скопируете этот вывод командной строке.) Сравните:
bash$ ssh-agent -c setenv SSH2_AUTH_SOCK /tmp/ssh-era/ssh2-10751-agent; setenv SSH2_AGENT_PID 10752; echo Agent pid 10752;
(Как видите,
csh
иtcsh
используетsetenv
установить varibles.)ваша собственная программа тоже может это сделать.
bash$ foo=$(makefoo)
код
makefoo
скрипт просто вычислит и напечатает значение, и пусть вызывающий абонент делает с ним все, что хочет-назначение его переменной является обычным случаем использования, но, вероятно, не то, что вы хотите жестко закодировать в инструмент, который производит значение.
это не то, что я бы назвал выдающимся, но это также работает, если вам нужно вызвать скрипт из оболочки в любом случае. Это не очень хорошее решение, но для одной статической переменной среды оно работает достаточно хорошо.
1.) Создайте скрипт с условием, выходящим либо из 0 (успешно), либо из 1 (Не успешно)
if [[ $foo == "True" ]]; then exit 0 else exit 1
2.) Создать псевдоним, который зависит от кода выхода.
alias='myscript.sh && export MyVariable'
вы вызываете псевдоним, который вызывает скрипт, который вычисляет условие, которое требуется для выхода из нуля через'&&', чтобы установить переменную окружения в родительской оболочке.
это обломки, но это может быть полезно в крайнем случае.