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

Что здесь происходит ( в современной ОС ), так это то, что ваша программа запускается внутри собственного «процесса». Это объект операционной системы, который наделен своим собственным адресным пространством, файловыми дескрипторами и т. Д. Ваши вызовы malloc выделяют память из «кучи» или нераспределенных страниц памяти, которые назначены вашему процессу.

Когда ваша программа заканчивается, как и в этом примере, все ресурсы, назначенные вашему процессу, просто перерабатываются или вырываются операционной системой. В случае памяти все страницы памяти, которые вам назначены, просто помечены как «свободные» и переработаны для использования других процессов. Страницы представляют собой концепцию более низкого уровня, чем то, что обрабатывает malloc - в результате, особенности malloc / free все просто смываются, поскольку все это очищается.

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

Все это сказало, как отмечают все остальные ответчики, полагаясь на это не очень хорошая практика:

  1. Вы всегда должны программировать забота о ресурсах, а также в C, что также означает память. Вы можете вставить свой код в библиотеку, или он может работать намного дольше, чем вы ожидаете.
  2. Некоторые ОС (более старые и, возможно, некоторые современные встроенные) могут не поддерживать такие жесткие границы процесса, и ваши распределения могут повлиять на адресные пространства других пользователей.
377
задан codeforester 31 August 2018 в 16:20
поделиться

21 ответ

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

241
ответ дан converter42 31 August 2018 в 16:20
поделиться

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

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

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

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

alias='myscript.sh && export MyVariable'

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

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

0
ответ дан user1802263 31 August 2018 в 16:20
поделиться

В OS X bash вы можете сделать следующее:
Создать файл сценария bash для сброса переменной

#!/bin/bash
unset http_proxy

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

sudo chmod 744 unsetvar

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

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

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

1
ответ дан Marton Tatai 31 August 2018 в 16:20
поделиться

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

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

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

while IFS= read -r -d  

Если родительской оболочкой является дефис, то 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
\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
3
ответ дан Community 31 August 2018 в 16:20
поделиться

Добавьте флаг -l вверху вашего bash-скрипта, т.е.

#!/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).
4
ответ дан cristobal 31 August 2018 в 16:20
поделиться

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

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

ln -s setit setit-sh

и

ln -s setit setit-csh

Теперь или непосредственно или в псевдониме, Вы делаете это от sh

eval `setit-sh`

или это от csh

eval `setit-csh`

setit использует 0$ для определения его выходного стиля.

Это является напоминающим о том, как люди используют для получения ТЕРМИНА переменная среды.

Преимущество здесь состоит в том, что 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

с символьными ссылками, данными выше, и оценка backquoted выражения, это имеет желаемый результат.

Упростить вызов для csh, tcsh, или подобные оболочки:

alias dosetit 'eval `setit-csh`'

или для sh, удара, и т.п.:

alias dosetit='eval `setit-sh`'

Одна хорошая вещь об этом состоит в том, что только необходимо вести список в одном месте. В теории Вы могли даже прикрепить список в файл и поместить cat nvpairfilename между "в" и "делают".

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

53
ответ дан Thomas Kammeyer 31 August 2018 в 16:20
поделиться

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

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

2
ответ дан Oleg Chirukhin 31 August 2018 в 16:20
поделиться

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

Ваша собственная программа может сделать это тоже.

bash$ foo=$(makefoo)

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

0
ответ дан tripleee 31 August 2018 в 16:20
поделиться

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

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

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

1
ответ дан sancho.s 31 August 2018 в 16:20
поделиться

Это «своего рода» возможно благодаря использованию 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
26
ответ дан Kjetil Joergensen 31 August 2018 в 16:20
поделиться

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

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

10
ответ дан Davide 31 August 2018 в 16:20
поделиться

Используйте синтаксис вызова «сценарий точечного пространства». Например, вот как это сделать, используя полный путь к скрипту:

. /path/to/set_env_vars.sh

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

. set_env_vars.sh

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

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

290
ответ дан Alan W. Smith 31 August 2018 в 16:20
поделиться

Кроме записи условных выражений, в зависимости от того, что установлено в $ SHELL / $ TERM, нет. Что не так с использованием Perl? Это довольно вездесущий (я не могу думать об одном варианте UNIX, у которого его нет), и он избавит вас от хлопот.

-9
ответ дан phresus 31 August 2018 в 16:20
поделиться

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

alias your_env='source ~/scripts/your_env.sh'
.
1
ответ дан Garrett Hyde 31 August 2018 в 16:20
поделиться

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

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

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

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

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

1
ответ дан David Lovering 31 August 2018 в 16:20
поделиться

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

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

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

Тогда, когда вы закончите с окружающей средой, просто exit.

1
ответ дан Andrew 31 August 2018 в 16:20
поделиться

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

Я столкнулся с очень похожей проблемой, в которой я хотел иметь возможность выполнить последний набор тестов (вместо всех моих тестов). Мой первый план состоял в том, чтобы написать одну команду для установки переменной 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
8
ответ дан dkinzer 31 August 2018 в 16:20
поделиться

Я создал решение, используя трубы, 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

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

1
ответ дан Luiz Angelo Daros de Luca 31 August 2018 в 16:20
поделиться

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

1
ответ дан Howard Hobbes 31 August 2018 в 16:20
поделиться

Это работает & mdash; это не то, что я бы использовал, но это «работает». Давайте создадим скрипт 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 для создания интерактивной оболочки). Вы также можете добавить "$@" после опции для передачи любых других аргументов, что может сделать оболочку пригодной для использования в качестве общего инструмента 'set environment and execute command'. Возможно, вы захотите опустить -i, если есть другие аргументы, приводящие к:

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

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

12
ответ дан Jonathan Leffler 31 August 2018 в 16:20
поделиться

В моем .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
}

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

45
ответ дан GKFX 31 August 2018 в 16:20
поделиться