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

Я пытаюсь написать оболочку script, которая при запуске установит некоторые переменные среды, которые останутся установленными в оболочке вызывающего.

setenv FOO foo

в csh/tcsh или

export FOO=foo

в sh/ bash устанавливает его только во время выполнения script.

Я уже знаю, что

source myscript

будет запускать команды script, а не запускать новую оболочку, и это может привести к установке среды "вызывающего".

Но здесь rub:

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

Есть ли разумное решение, которое не связано с необходимостью писать и поддерживать TWO-версии на script?

Ответ 1

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

Ответ 2

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

. /path/to/set_env_vars.sh

И вот как это сделать, если вы находитесь в том же каталоге, что и script:

. set_env_vars.sh

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

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

Ответ 3

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

Одна вещь, которую вы можете сделать, это написать script, который испускает правильные команды для tcsh или sh основывается на том, как он вызывается. Если вы script является "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".

Это в значительной степени связано с тем, как выполнялись параметры терминала для входа в систему: script выводит статуты, которые будут выполняться в оболочке входа. Псевдоним, как правило, использовался бы, чтобы сделать обращение простым, как в "tset vt100". Как уже упоминалось в другом ответе, на сервере новостей INN UseNet есть аналогичные функции.

Ответ 4

В моем .bash_profile у меня есть:

# 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
}

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

Ответ 5

Это "возможно" с помощью 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

Ответ 6

Это работает — это не то, что я бы использовал, но он "работает". Позвольте создать script teredo, чтобы установить переменную среды TEREDO_WORMS:

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

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

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

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

Когда выполняется script, вы находитесь в новой оболочке, другой интерактивной оболочке 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. Вы можете обнаружить, что приглашение после команд выхода появляется в забавных местах.


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

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

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

Ответ 7

Вы должны использовать модули, см. http://modules.sourceforge.net/

EDIT: пакет модулей не обновляется с 2012 года, но все еще работает нормально для основ. Все новые функции, колокола и свистки происходят в lmod в этот день (что мне больше нравится): https://www.tacc.utexas.edu/research-development/tacc-projects/lmod

Ответ 8

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

Я столкнулся с очень похожим вопросом, где мне захотелось запустить последний тестовый тест (а не все мои тесты). Мой первый план состоял в том, чтобы написать одну команду для установки переменной env TESTCASE, а затем другую команду, которая использовала бы это для запуска теста. Излишне говорить, что у меня была такая же точная проблема, как и вы.

Но потом я придумал этот простой взлом:

Первая команда (testset):

#!/bin/bash

if [ $# -eq 1 ]
then
  echo $1 > ~/.TESTCASE
  echo "TESTCASE has been set to: $1"
else
  echo "Come again?"
fi

Вторая команда (testrun):

#!/bin/bash

TESTCASE=$(cat ~/.TESTCASE)
drush test-run $TESTCASE

Ответ 9

Добавьте флаг -l в начало bash script i.e.

#!/usr/bin/env bash -l

...

export NAME1="VALUE1"
export NAME2="VALUE2"

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

Из справочных страниц:

-l Make bash act as if it had been invoked as a login shell (see INVOCATION below).

Ответ 10

Вы можете указать дочернему процессу распечатать переменные среды (путем вызова "env" ), затем перебрать переменные печатной среды в родительском процессе и вызвать "экспорт" этих переменных.

Следующий код основан на Захват вывода find. -print0 в массив bash

Если родительская оболочка является bash, вы можете использовать

while IFS= read -r -d $'\0' 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 $'\0' 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

Ответ 11

Вы можете вызвать еще один Bash с помощью другого bash_profile. Кроме того, вы можете создать специальный файл bash_profile для использования в среде multi-bashprofile.

Помните, что вы можете использовать функции внутри bashprofile, и эти функции будут доступны во всем мире. например, "функция user {export USER_NAME $1}" может устанавливать переменную во время выполнения, например: user olegchir && & env | grep olegchir

Ответ 12

Технически, это правильно - только "eval" не разветвляет другую оболочку. Однако с точки зрения приложения, которое вы пытаетесь запустить в измененной среде, разница равна nil: ребенок наследует среду своего родителя, поэтому (измененная) среда передается всем нисходящим процессам.

Ipso facto, измененная переменная среды "sticks" - пока вы работаете под родительской программой/оболочкой.

Если необходимо, чтобы переменная среды оставалась после выхода родительского (Perl или shell), необходимо, чтобы родительская оболочка выполняла тяжелый подъем. Один из методов, который я видел в документации, - это текущий script, чтобы порождать исполняемый файл с необходимым языком "export", а затем обмануть родительскую оболочку в ее выполнение - всегда сознавая тот факт, что вам нужно предисловие к команде с "источником", если вы пытаетесь оставить не изменчивую версию измененной среды. Клюдж в лучшем случае.

Второй способ - изменить script, который инициирует среду оболочки (.bashrc или что-то еще), чтобы содержать модифицированный параметр. Это может быть опасно - если вы запустили инициализацию script, это может сделать вашу оболочку недоступной при следующей попытке запуска. Существует множество инструментов для модификации текущей оболочки; добавив необходимые настройки в "пусковую установку", вы также эффективно продвигаете эти изменения вперед. Как правило, это не очень хорошая идея; если вам нужны только изменения среды для конкретного пакета приложений, вам придется вернуться и вернуть запуск оболочки script в свое первоначальное состояние (используя vi или что-то еще).

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

Ответ 13

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

Так почему бы не просто что-то вроде

#!/usr/bin/env bash
FOO=foo $SHELL

Затем, когда вы закончите работу с окружением, просто exit.

Ответ 14

Вы всегда можете использовать псевдонимы

alias your_env='source ~/scripts/your_env.sh'

Ответ 15

Другой вариант - использовать "Модули среды" (http://modules.sourceforge.net/). К сожалению, в этот микс входит третий язык. Вы определяете среду с языком Tcl, но есть несколько удобных команд для типичных модификаций (preend vs. append vs set). Вам также потребуется установить модули среды. Затем вы можете использовать module load *XXX*, чтобы назвать нужную среду. Команда модуля в основном представляет собой причудливый псевдоним для механизма eval, описанного выше Томасом Каммейером. Главное преимущество здесь в том, что вы можете поддерживать среду на одном языке и полагаться на "Модули среды", чтобы перевести ее в sh, ksh, bash, csh, tcsh, zsh, python (?!?!!) и т.д.

Ответ 16

Я сделал это много лет назад. Если я правильно помню, я включил псевдоним в каждом из .bashrc и .cshrc с параметрами, наложив соответствующие формы настройки среды на общую форму.

Затем script, который вы укажете в любой из двух оболочек, имеет команду с этой последней формой, которая подходит для каждой оболочки с псевдонимом.

Если я найду конкретные псевдонимы, я опубликую их.

Ответ 17

Я создал решение, используя трубы, eval и signal.

parent() {
    if [ -z "$G_EVAL_FD" ]; then
            die 1 "Rode primeiro parent_setup no processo pai"
    fi
    if [ $(ppid) = "$$" ]; then
            "[email protected]"
    else
            kill -SIGUSR1 $$
            echo "[email protected]">&$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

Он может работать с любой командой.

Ответ 18

В OS X bash вы можете сделать следующее:

Создайте файл bash script, чтобы отключить переменную

#!/bin/bash
unset http_proxy

Сделать исполняемый файл

sudo chmod 744 unsetvar

Создать псевдоним

alias unsetvar='source /your/path/to/the/script/unsetvar'

Он должен быть готов к использованию так долго, у вас есть папка, содержащая ваш файл script, добавленный к пути.

Ответ 19

Я не вижу ответа, документируя, как обойти эту проблему с взаимодействующими процессами. Общий шаблон с такими вещами, как 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 script просто вычислил и распечатал значение, и позвольте вызывающему абоненту делать то, что они хотят с ним - назначение его переменной является обычным прецедентом, но, вероятно, не то, код в инструмент, который производит значение.

Ответ 20

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

1.) Создайте скрипт с условием, которое завершает либо 0 (успешное), либо 1 (не успешно)

if [[ $foo == "True" ]]; then
    exit 0
else
    exit 1

2.) Создайте псевдоним, который зависит от кода выхода.

alias='myscript.sh && export MyVariable'

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

Это flotsam, но это может быть полезно в крайнем случае.

Ответ 21

За исключением условий записи в зависимости от того, для чего установлено значение $SHELL/$TERM, no. Что случилось с использованием Perl? Это довольно повсеместно (я не могу придумать ни одного варианта UNIX, у которого его нет), и это избавит вас от неприятностей.