shell脚本可以设置调用shell的环境变量吗?

时间:2022-05-18 11:43:30

I'm trying to write a shell script that, when run, will set some environment variables that will stay set in the caller's shell.

我正在编写一个shell脚本,在运行时,它将设置一些环境变量,这些变量将保持在调用者的shell中。

setenv FOO foo

in csh/tcsh, or

在csh / tcsh,或

export FOO=foo

in sh/bash only set it during the script's execution.

在sh/bash中,只在脚本执行期间设置它。

I already know that

我已经知道了

source myscript

will run the commands of the script rather than launching a new shell, and that can result in setting the "caller's" environment.

将运行脚本的命令,而不是启动新的shell,这将导致设置“调用者”环境。

But here's the rub:

但是这是摩擦:

I want this script to be callable from either bash or csh. In other words, I want users of either shell to be able to run my script and have their shell's environment changed. So 'source' won't work for me, since a user running csh can't source a bash script, and a user running bash can't source a csh script.

我希望这个脚本可以从bash或csh调用。换句话说,我希望任何一个shell的用户都能够运行我的脚本并更改他们的shell环境。因此,“source”对我不起作用,因为运行csh的用户不能提供bash脚本的源代码,运行bash的用户不能提供csh脚本的源代码。

Is there any reasonable solution that doesn't involve having to write and maintain TWO versions on the script?

有什么合理的解决方案不需要在脚本上编写和维护两个版本吗?

20 个解决方案

#1


207  

Your shell process has a copy of the parent's environment and no access to the parent process's environment whatsoever. When your shell process terminates any changes you've made to its environment are lost. Sourcing a script file is the most commonly used method for configuring a shell environment, you may just want to bite the bullet and maintain one for each of the two flavors of shell.

您的shell进程有父进程环境的副本,并且没有访问父进程环境的权限。当您的shell进程终止对其环境所做的任何更改时,将丢失。寻找脚本文件是配置shell环境最常用的方法,您可能只想咬紧牙关,为shell的两种风格分别维护一个脚本文件。

#2


171  

Use the "dot space script" calling syntax. For example, here's how to do it using the full path to a script:

使用“点空间脚本”调用语法。例如,以下是如何使用脚本的完整路径:

. /path/to/set_env_vars.sh

And here's how to do it if you're in the same directory as the script:

下面是如何做到这一点,如果你和脚本在同一个目录下:

. set_env_vars.sh

These execute the script under the current shell instead of loading another one (which is what would happen if you did ./set_env_vars.sh). Because it runs in the same shell, the environmental variables you set will be available when it exits.

它们在当前shell下执行脚本,而不是加载另一个脚本(如果这样做了,就会发生这种情况。/set_env_var .sh)。因为它在相同的shell中运行,所以您设置的环境变量将在它退出时可用。

This is the same thing as calling source set_env_vars.sh, but it's shorter to type and might work in some places where source doesn't.

这与调用源set_env_vars是一样的。sh,但它的类型更短,而且可能在一些源代码没有的地方工作。

#3


50  

You're not going to be able to modify the caller's shell because it's in a different process context. When child processes inherit your shell's variables, they're inheriting copies themselves.

您将无法修改调用者的shell,因为它位于不同的流程上下文中。当子进程继承shell的变量时,它们将自己继承副本。

One thing you can do is to write a script that emits the correct commands for tcsh or sh based how it's invoked. If you're script is "setit" then do:

您可以做的一件事是编写一个脚本,根据tcsh或sh的调用方式发出正确的命令。如果你的剧本是“setit”,那就说:

ln -s setit setit-sh

and

ln -s setit setit-csh

Now either directly or in an alias, you do this from sh

现在你可以直接或者用别名,从sh开始

eval `setit-sh`

or this from csh

或从csh

eval `setit-csh`

setit uses $0 to determine its output style.

setit使用$0来确定它的输出样式。

This is reminescent of how people use to get the TERM environment variable set.

这提醒了人们如何使用环境变量集这个术语。

The advantage here is that setit is just written in whichever shell you like as in:

这里的优点是,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

with the symbolic links given above, and the eval of the backquoted expression, this has the desired result.

有了上面给出的符号链接和后引号表达式的值,这就得到了想要的结果。

To simplify invocation for csh, tcsh, or similar shells:

简化对csh、tcsh或类似shell的调用:

alias dosetit 'eval `setit-csh`'

or for sh, bash, and the like:

或者对于sh, bash等:

alias dosetit='eval `setit-sh`'

One nice thing about this is that you only have to maintain the list in one place. In theory you could even stick the list in a file and put cat nvpairfilename between "in" and "do".

这样做的一个好处是,您只需在一个地方维护列表。理论上,你甚至可以把列表放在一个文件中,然后把cat nvpairfilename放在“In”和“do”之间。

This is pretty much how login shell terminal settings used to be done: a script would output statments to be executed in the login shell. An alias would generally be used to make invocation simple, as in "tset vt100". As mentioned in another answer, there is also similar functionality in the INN UseNet news server.

这几乎是登录shell终端设置的方式:脚本将在登录shell中执行要执行的状态。别名通常用于简化调用,如“tset vt100”。正如在另一个答案中提到的,INN UseNet新闻服务器也有类似的功能。

#4


44  

In my .bash_profile I have :

在我的。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
}

So when I want to disable the proxy, the function(s) run in the login shell and sets the variables as expected and wanted.

因此,当我要禁用代理时,函数在登录shell中运行,并按预期设置变量。

#5


21  

It's "kind of" possible through using gdb and setenv(3), although I have a hard time recommending actually doing this. (Additionally, i.e. the most recent ubuntu won't actually let you do this without telling the kernel to be more permissive about ptrace, and the same may go for other distros as well).

通过使用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


12  

This works — it isn't what I'd use, but it 'works'. Let's create a script teredo to set the environment variable TEREDO_WORMS:

这是有效的-它不是我要用的,但它是有效的。让我们创建一个脚本teredo来设置环境变量teredo_worm:

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

It will be interpreted by the Korn shell, exports the environment variable, and then replaces itself with a new interactive shell.

它将由Korn shell解释,导出环境变量,然后用一个新的交互式shell替换自己。

Before running this script, we have SHELL set in the environment to the C shell, and the environment variable TEREDO_WORMS is not set:

在运行这个脚本之前,我们在环境中将SHELL设置为C SHELL,没有设置环境变量teredo_worm:

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

When the script is run, you are in a new shell, another interactive C shell, but the environment variable is set:

当脚本运行时,您处于另一个交互式C shell中,但是环境变量设置为:

% teredo
% env | grep TEREDO
TEREDO_WORMS=ukelele
%

When you exit from this shell, the original shell takes over:

当你离开这个壳层时,原来的壳层接管:

% exit
% env | grep TEREDO
%

The environment variable is not set in the original shell's environment. If you use exec teredo to run the command, then the original interactive shell is replaced by the Korn shell that sets the environment, and then that in turn is replaced by a new interactive C shell:

环境变量没有设置在原始shell的环境中。如果使用exec teredo运行命令,那么原来的交互式shell将被设置环境的Korn shell所取代,然后这个shell将被新的交互式C shell所取代:

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

If you type exit (or Control-D), then your shell exits, probably logging you out of that window, or taking you back to the previous level of shell from where the experiments started.

如果您输入exit(或Control-D),那么您的shell将退出,可能会将您记录到该窗口之外,或者将您带回实验开始的shell的前一层。

The same mechanism works for Bash or Korn shell. You may find that the prompt after the exit commands appears in funny places.

同样的机制也适用于Bash或Korn shell。您可能会发现,退出命令后的提示符出现在有趣的地方。


Note the discussion in the comments. This is not a solution I would recommend, but it does achieve the stated purpose of a single script to set the environment that works with all shells (that accept the -i option to make an interactive shell). You could also add "$@" after the option to relay any other arguments, which might then make the shell usable as a general 'set environment and execute command' tool. You might want to omit the -i if there are other arguments, leading to:

注意评论中的讨论。这不是我推荐的解决方案,但它确实实现了单个脚本的目的,以设置与所有shell一起工作的环境(接受-i选项以创建交互式shell)。您还可以在选项之后添加“$@”,以传递任何其他参数,这样外壳就可以作为通用的“设置环境和执行命令”工具使用。如果有其他参数,您可能想要省略i,导致:

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

The "${@-'-i'}" bit means 'if the argument list contains at least one argument, use the original argument list; otherwise, substitute -i for the non-existent arguments'.

“${@-'-i'}”位表示“如果参数列表包含至少一个参数,则使用原始参数列表;否则,用-i替换不存在的参数。

#7


11  

You should use modules, see http://modules.sourceforge.net/

您应该使用模块,请参见http://modules.sourceforge.net/

EDIT: The modules package has not been updated since 2012 but still works ok for the basics. All the new features, bells and whistles happen in lmod this day (which I like it more): https://www.tacc.utexas.edu/research-development/tacc-projects/lmod

编辑:模块包自2012年以来没有更新过,但是对于基础来说仍然适用。所有的新功能,铃声和哨声都会在今天出现在lmod(我更喜欢它):https://www.tacc.utexas.edu/research-development/tacc-projects/lmod

#8


6  

Another workaround that I don't see mentioned is to write the variable value to a file.

我没有提到的另一个解决方法是将变量值写入文件。

I ran into a very similar issue where I wanted to be able to run the last set test (instead of all my tests). My first plan was to write one command for setting the env variable TESTCASE, and then have another command that would use this to run the test. Needless to say that I had the same exact issue as you did.

我遇到了一个非常类似的问题,我希望能够运行最后一个set测试(而不是所有的测试)。我的第一个计划是编写一个命令来设置env变量TESTCASE,然后再编写另一个命令来运行测试。不用说,我和你有同样的问题。

But then I came up with this simple hack:

但后来我想到了一个简单的办法:

First command ( testset ):

第一个命令(testset):

#!/bin/bash

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

Second command (testrun ):

第二个命令(testrun):

#!/bin/bash

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

#9


3  

Add the -l flag in top of your bash script i.e.

在bash脚本的顶部添加-l标志,例如。

#!/usr/bin/env bash -l

...

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

The values with NAME1 and NAME2 will now have been exported to your current environment, however these changes are not permanent. If you want them to be permanent you need to add them to your .bashrc file or other init file.

NAME1和NAME2的值现在将被导出到您当前的环境中,但是这些更改并不是永久的。如果希望它们是永久性的,需要将它们添加到.bashrc文件或其他init文件中。

From the man pages:

从手册页:

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

#10


3  

You can instruct the child process to print its environment variables (by calling "env"), then loop over the printed environment variables in the parent process and call "export" on those variables.

您可以指示子进程打印它的环境变量(通过调用“env”),然后在父进程中循环打印环境变量,并对这些变量调用“export”。

The following code is based on Capturing output of find . -print0 into a bash array

下面的代码基于find的捕获输出。-print0到bash数组中

If the parent shell is the bash, you can use

如果父shell是bash,则可以使用

while IFS= read -r -d $'\0' line; do
    export "$line"
done < <(bash -s <<< 'export VARNAME=something; env -0')
echo $VARNAME

If the parent shell is the dash, then read does not provide the -d flag and the code gets more complicated

如果父shell是dash,那么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


2  

You can invoke another one Bash with the different bash_profile. Also, you can create special bash_profile for using in multi-bashprofile environment.

您可以使用不同的bash_profile调用另一个Bash。此外,您还可以创建用于多bashprofile环境的特殊bash_profile。

Remember that you can use functions inside of bashprofile, and that functions will be avialable globally. for example, "function user { export USER_NAME $1 }" can set variable in runtime, for example: user olegchir && env | grep olegchir

请记住,您可以在bashprofile中使用函数,该函数将在全局范围内可用。例如,“函数用户{export USER_NAME $1}”可以在运行时设置变量,例如:user olegchir & env | grep olegchir

#12


1  

Technically, that is correct -- only 'eval' doesn't fork another shell. However, from the point of view of the application you're trying to run in the modified environment, the difference is nil: the child inherits the environment of its parent, so the (modified) environment is conveyed to all descending processes.

从技术上讲,这是正确的——只有“eval”不会派生另一个shell。但是,从您试图在修改后的环境中运行的应用程序的角度来看,差异为nil:子进程继承其父进程的环境,因此(修改后的)环境被传递给所有下行进程。

Ipso facto, the changed environment variable 'sticks' -- as long as you are running under the parent program/shell.

事实上,更改后的环境变量“stick”—只要您在父程序/shell下运行。

If it is absolutely necessary for the environment variable to remain after the parent (Perl or shell) has exited, it is necessary for the parent shell to do the heavy lifting. One method I've seen in the documentation is for the current script to spawn an executable file with the necessary 'export' language, and then trick the parent shell into executing it -- always being cognizant of the fact that you need to preface the command with 'source' if you're trying to leave a non-volatile version of the modified environment behind. A Kluge at best.

如果环境变量在父(Perl或shell)退出后仍然是绝对必要的,那么父shell就必须执行繁重的任务。文档我看过的一个方法是对当前脚本生成一个可执行文件进行必要的“出口”语言,然后诱骗父shell执行它,总是被认识到的事实需要序言命令与“源”如果你想留下的非易失性版本修改的环境。一个拼凑起来的。

The second method is to modify the script that initiates the shell environment (.bashrc or whatever) to contain the modified parameter. This can be dangerous -- if you hose up the initialization script it may make your shell unavailable the next time it tries to launch. There are plenty of tools for modifying the current shell; by affixing the necessary tweaks to the 'launcher' you effectively push those changes forward as well. Generally not a good idea; if you only need the environment changes for a particular application suite, you'll have to go back and return the shell launch script to its pristine state (using vi or whatever) afterwards.

第二个方法是修改启动shell环境的脚本(。bashrc或其他)以包含修改后的参数。这可能是危险的——如果您将初始化脚本连接起来,它可能会使您的shell在下一次尝试启动时无法使用。有很多工具可以修改当前的shell;通过对“启动器”进行必要的调整,你可以有效地推动这些变化。通常不是个好主意;如果您只需要对特定应用程序套件进行环境更改,那么您必须返回并将shell启动脚本返回到其原始状态(使用vi或其他)。

In short, there are no good (and easy) methods. Presumably this was made difficult to ensure the security of the system was not irrevocably compromised.

简而言之,没有好的(简单的)方法。这可能使确保系统的安全性不受不可挽回的损害变得困难。

#13


1  

The short answer is no, you cannot alter the environment of the parent process, but it seems like what you want is an environment with custom environment variables and the shell that the user has chosen.

简短的回答是不,您不能更改父进程的环境,但是看起来您想要的是一个具有自定义环境变量和用户选择的外壳的环境。

So why not simply something like

所以为什么不简单地像

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

Then when you are done with the environment, just exit.

然后,当您处理完环境后,只需退出。

#14


1  

You could always use aliases

你可以使用别名

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

#15


1  

Another option is to use "Environment Modules" (http://modules.sourceforge.net/). This unfortunately introduces a third language into the mix. You define the environment with the language of Tcl, but there are a few handy commands for typical modifications (prepend vs. append vs set). You will also need to have environment modules installed. You can then use module load *XXX* to name the environment you want. The module command is basically a fancy alias for the eval mechanism described above by Thomas Kammeyer. The main advantage here is that you can maintain the environment in one language and rely on "Environment Modules" to translate it to sh, ksh, bash, csh, tcsh, zsh, python (?!?!!), etc.

另一种选择是使用“环境模块”(http://modules.sourceforge.net/)。这不幸地引入了第三种语言。您使用Tcl语言定义环境,但是有一些用于典型修改的方便命令(prepend vs. append vs. set)。您还需要安装环境模块。然后可以使用模块load *XXX*来命名所需的环境。模块命令基本上是上面Thomas Kammeyer描述的eval机制的花哨别名。这里的主要优点是,您可以使用一种语言维护环境,并依赖“环境模块”将其转换为sh、ksh、bash、csh、tcsh、zsh、python(?! !!)等。

#16


1  

I did this many years ago. If I rememeber correctly, I included an alias in each of .bashrc and .cshrc, with parameters, aliasing the respective forms of setting the environment to a common form.

这是我多年前做的。如果我没记错的话,我在每个.bashrc和.cshrc中都包含了一个别名,其中包含了参数,以使将环境设置为公共形式的相应形式具有别名。

Then the script that you will source in any of the two shells has a command with that last form, that is suitable aliased in each shell.

然后,您将在任何两个shell中提供的脚本都有一个与最后一个表单相匹配的命令,该命令在每个shell中都是合适的。

If I find the concrete aliases, I will post them.

如果我找到具体的别名,我会贴上去。

#17


1  

I created a solution using pipes, eval and signal.

我使用管道、eval和signal创建了一个解决方案。

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

It might work with any command.

它可以使用任何命令。

#18


0  

Under OS X bash you can do the following:
Create the bash script file to unset the variable

在OS X bash下,您可以执行以下操作:创建bash脚本文件来取消变量的设置

#!/bin/bash
unset http_proxy

Make the file executable

使文件可执行

sudo chmod 744 unsetvar

Create alias

创建别名

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

It should be ready to use so long you have the folder containing your script file appended to the path.

它应该准备好使用这么长时间,您将包含脚本文件的文件夹附加到路径。

#19


0  

I don't see any answer documenting how to work around this problem with cooperating processes. A common pattern with things like ssh-agent is to have the child process print an expression which the parent can eval.

我没有看到任何关于如何使用协作过程来解决这个问题的答案。与ssh-agent类似的常见模式是让子进程打印父进程可以计算的表达式。

bash$ eval $(shh-agent)

For example, ssh-agent has options to select Csh or Bourne-compatible output syntax.

例如,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;

(This causes the agent to start running, but doesn't allow you to actually use it, unless you now copy-paste this output to your shell prompt.) Compare:

(这会导致代理开始运行,但不允许实际使用它,除非您现在将该输出复制到shell提示符。)比较:

bash$ ssh-agent -c
setenv SSH2_AUTH_SOCK /tmp/ssh-era/ssh2-10751-agent;
setenv SSH2_AGENT_PID 10752;
echo Agent pid 10752;

(As you can see, csh and tcsh uses setenv to set varibles.)

(如您所见,csh和tcsh使用setenv设置变量。)

Your own program can do this, too.

你自己的程序也可以做到这一点。

bash$ foo=$(makefoo)

Your makefoo script would simply calculate and print the value, and let the caller do whatever they want with it -- assigning it to a variable is a common use case, but probably not something you want to hard-code into the tool which produces the value.

您的makefoo脚本只需计算并打印值,并让调用者使用它做他们想做的任何事情——将它分配给一个变量是一个常见的用例,但可能不是您想硬编码到生成值的工具中的东西。

#20


-9  

Other than writings conditionals depending on what $SHELL/$TERM is set to, no. What's wrong with using Perl? It's pretty ubiquitous (I can't think of a single UNIX variant that doesn't have it), and it'll spare you the trouble.

除了根据$SHELL/$TERM设置的写作条件外,没有。使用Perl有什么问题?它是非常普遍的(我想不出一个没有它的UNIX变体),并且它将为您省去麻烦。

#1


207  

Your shell process has a copy of the parent's environment and no access to the parent process's environment whatsoever. When your shell process terminates any changes you've made to its environment are lost. Sourcing a script file is the most commonly used method for configuring a shell environment, you may just want to bite the bullet and maintain one for each of the two flavors of shell.

您的shell进程有父进程环境的副本,并且没有访问父进程环境的权限。当您的shell进程终止对其环境所做的任何更改时,将丢失。寻找脚本文件是配置shell环境最常用的方法,您可能只想咬紧牙关,为shell的两种风格分别维护一个脚本文件。

#2


171  

Use the "dot space script" calling syntax. For example, here's how to do it using the full path to a script:

使用“点空间脚本”调用语法。例如,以下是如何使用脚本的完整路径:

. /path/to/set_env_vars.sh

And here's how to do it if you're in the same directory as the script:

下面是如何做到这一点,如果你和脚本在同一个目录下:

. set_env_vars.sh

These execute the script under the current shell instead of loading another one (which is what would happen if you did ./set_env_vars.sh). Because it runs in the same shell, the environmental variables you set will be available when it exits.

它们在当前shell下执行脚本,而不是加载另一个脚本(如果这样做了,就会发生这种情况。/set_env_var .sh)。因为它在相同的shell中运行,所以您设置的环境变量将在它退出时可用。

This is the same thing as calling source set_env_vars.sh, but it's shorter to type and might work in some places where source doesn't.

这与调用源set_env_vars是一样的。sh,但它的类型更短,而且可能在一些源代码没有的地方工作。

#3


50  

You're not going to be able to modify the caller's shell because it's in a different process context. When child processes inherit your shell's variables, they're inheriting copies themselves.

您将无法修改调用者的shell,因为它位于不同的流程上下文中。当子进程继承shell的变量时,它们将自己继承副本。

One thing you can do is to write a script that emits the correct commands for tcsh or sh based how it's invoked. If you're script is "setit" then do:

您可以做的一件事是编写一个脚本,根据tcsh或sh的调用方式发出正确的命令。如果你的剧本是“setit”,那就说:

ln -s setit setit-sh

and

ln -s setit setit-csh

Now either directly or in an alias, you do this from sh

现在你可以直接或者用别名,从sh开始

eval `setit-sh`

or this from csh

或从csh

eval `setit-csh`

setit uses $0 to determine its output style.

setit使用$0来确定它的输出样式。

This is reminescent of how people use to get the TERM environment variable set.

这提醒了人们如何使用环境变量集这个术语。

The advantage here is that setit is just written in whichever shell you like as in:

这里的优点是,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

with the symbolic links given above, and the eval of the backquoted expression, this has the desired result.

有了上面给出的符号链接和后引号表达式的值,这就得到了想要的结果。

To simplify invocation for csh, tcsh, or similar shells:

简化对csh、tcsh或类似shell的调用:

alias dosetit 'eval `setit-csh`'

or for sh, bash, and the like:

或者对于sh, bash等:

alias dosetit='eval `setit-sh`'

One nice thing about this is that you only have to maintain the list in one place. In theory you could even stick the list in a file and put cat nvpairfilename between "in" and "do".

这样做的一个好处是,您只需在一个地方维护列表。理论上,你甚至可以把列表放在一个文件中,然后把cat nvpairfilename放在“In”和“do”之间。

This is pretty much how login shell terminal settings used to be done: a script would output statments to be executed in the login shell. An alias would generally be used to make invocation simple, as in "tset vt100". As mentioned in another answer, there is also similar functionality in the INN UseNet news server.

这几乎是登录shell终端设置的方式:脚本将在登录shell中执行要执行的状态。别名通常用于简化调用,如“tset vt100”。正如在另一个答案中提到的,INN UseNet新闻服务器也有类似的功能。

#4


44  

In my .bash_profile I have :

在我的。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
}

So when I want to disable the proxy, the function(s) run in the login shell and sets the variables as expected and wanted.

因此,当我要禁用代理时,函数在登录shell中运行,并按预期设置变量。

#5


21  

It's "kind of" possible through using gdb and setenv(3), although I have a hard time recommending actually doing this. (Additionally, i.e. the most recent ubuntu won't actually let you do this without telling the kernel to be more permissive about ptrace, and the same may go for other distros as well).

通过使用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


12  

This works — it isn't what I'd use, but it 'works'. Let's create a script teredo to set the environment variable TEREDO_WORMS:

这是有效的-它不是我要用的,但它是有效的。让我们创建一个脚本teredo来设置环境变量teredo_worm:

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

It will be interpreted by the Korn shell, exports the environment variable, and then replaces itself with a new interactive shell.

它将由Korn shell解释,导出环境变量,然后用一个新的交互式shell替换自己。

Before running this script, we have SHELL set in the environment to the C shell, and the environment variable TEREDO_WORMS is not set:

在运行这个脚本之前,我们在环境中将SHELL设置为C SHELL,没有设置环境变量teredo_worm:

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

When the script is run, you are in a new shell, another interactive C shell, but the environment variable is set:

当脚本运行时,您处于另一个交互式C shell中,但是环境变量设置为:

% teredo
% env | grep TEREDO
TEREDO_WORMS=ukelele
%

When you exit from this shell, the original shell takes over:

当你离开这个壳层时,原来的壳层接管:

% exit
% env | grep TEREDO
%

The environment variable is not set in the original shell's environment. If you use exec teredo to run the command, then the original interactive shell is replaced by the Korn shell that sets the environment, and then that in turn is replaced by a new interactive C shell:

环境变量没有设置在原始shell的环境中。如果使用exec teredo运行命令,那么原来的交互式shell将被设置环境的Korn shell所取代,然后这个shell将被新的交互式C shell所取代:

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

If you type exit (or Control-D), then your shell exits, probably logging you out of that window, or taking you back to the previous level of shell from where the experiments started.

如果您输入exit(或Control-D),那么您的shell将退出,可能会将您记录到该窗口之外,或者将您带回实验开始的shell的前一层。

The same mechanism works for Bash or Korn shell. You may find that the prompt after the exit commands appears in funny places.

同样的机制也适用于Bash或Korn shell。您可能会发现,退出命令后的提示符出现在有趣的地方。


Note the discussion in the comments. This is not a solution I would recommend, but it does achieve the stated purpose of a single script to set the environment that works with all shells (that accept the -i option to make an interactive shell). You could also add "$@" after the option to relay any other arguments, which might then make the shell usable as a general 'set environment and execute command' tool. You might want to omit the -i if there are other arguments, leading to:

注意评论中的讨论。这不是我推荐的解决方案,但它确实实现了单个脚本的目的,以设置与所有shell一起工作的环境(接受-i选项以创建交互式shell)。您还可以在选项之后添加“$@”,以传递任何其他参数,这样外壳就可以作为通用的“设置环境和执行命令”工具使用。如果有其他参数,您可能想要省略i,导致:

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

The "${@-'-i'}" bit means 'if the argument list contains at least one argument, use the original argument list; otherwise, substitute -i for the non-existent arguments'.

“${@-'-i'}”位表示“如果参数列表包含至少一个参数,则使用原始参数列表;否则,用-i替换不存在的参数。

#7


11  

You should use modules, see http://modules.sourceforge.net/

您应该使用模块,请参见http://modules.sourceforge.net/

EDIT: The modules package has not been updated since 2012 but still works ok for the basics. All the new features, bells and whistles happen in lmod this day (which I like it more): https://www.tacc.utexas.edu/research-development/tacc-projects/lmod

编辑:模块包自2012年以来没有更新过,但是对于基础来说仍然适用。所有的新功能,铃声和哨声都会在今天出现在lmod(我更喜欢它):https://www.tacc.utexas.edu/research-development/tacc-projects/lmod

#8


6  

Another workaround that I don't see mentioned is to write the variable value to a file.

我没有提到的另一个解决方法是将变量值写入文件。

I ran into a very similar issue where I wanted to be able to run the last set test (instead of all my tests). My first plan was to write one command for setting the env variable TESTCASE, and then have another command that would use this to run the test. Needless to say that I had the same exact issue as you did.

我遇到了一个非常类似的问题,我希望能够运行最后一个set测试(而不是所有的测试)。我的第一个计划是编写一个命令来设置env变量TESTCASE,然后再编写另一个命令来运行测试。不用说,我和你有同样的问题。

But then I came up with this simple hack:

但后来我想到了一个简单的办法:

First command ( testset ):

第一个命令(testset):

#!/bin/bash

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

Second command (testrun ):

第二个命令(testrun):

#!/bin/bash

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

#9


3  

Add the -l flag in top of your bash script i.e.

在bash脚本的顶部添加-l标志,例如。

#!/usr/bin/env bash -l

...

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

The values with NAME1 and NAME2 will now have been exported to your current environment, however these changes are not permanent. If you want them to be permanent you need to add them to your .bashrc file or other init file.

NAME1和NAME2的值现在将被导出到您当前的环境中,但是这些更改并不是永久的。如果希望它们是永久性的,需要将它们添加到.bashrc文件或其他init文件中。

From the man pages:

从手册页:

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

#10


3  

You can instruct the child process to print its environment variables (by calling "env"), then loop over the printed environment variables in the parent process and call "export" on those variables.

您可以指示子进程打印它的环境变量(通过调用“env”),然后在父进程中循环打印环境变量,并对这些变量调用“export”。

The following code is based on Capturing output of find . -print0 into a bash array

下面的代码基于find的捕获输出。-print0到bash数组中

If the parent shell is the bash, you can use

如果父shell是bash,则可以使用

while IFS= read -r -d $'\0' line; do
    export "$line"
done < <(bash -s <<< 'export VARNAME=something; env -0')
echo $VARNAME

If the parent shell is the dash, then read does not provide the -d flag and the code gets more complicated

如果父shell是dash,那么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


2  

You can invoke another one Bash with the different bash_profile. Also, you can create special bash_profile for using in multi-bashprofile environment.

您可以使用不同的bash_profile调用另一个Bash。此外,您还可以创建用于多bashprofile环境的特殊bash_profile。

Remember that you can use functions inside of bashprofile, and that functions will be avialable globally. for example, "function user { export USER_NAME $1 }" can set variable in runtime, for example: user olegchir && env | grep olegchir

请记住,您可以在bashprofile中使用函数,该函数将在全局范围内可用。例如,“函数用户{export USER_NAME $1}”可以在运行时设置变量,例如:user olegchir & env | grep olegchir

#12


1  

Technically, that is correct -- only 'eval' doesn't fork another shell. However, from the point of view of the application you're trying to run in the modified environment, the difference is nil: the child inherits the environment of its parent, so the (modified) environment is conveyed to all descending processes.

从技术上讲,这是正确的——只有“eval”不会派生另一个shell。但是,从您试图在修改后的环境中运行的应用程序的角度来看,差异为nil:子进程继承其父进程的环境,因此(修改后的)环境被传递给所有下行进程。

Ipso facto, the changed environment variable 'sticks' -- as long as you are running under the parent program/shell.

事实上,更改后的环境变量“stick”—只要您在父程序/shell下运行。

If it is absolutely necessary for the environment variable to remain after the parent (Perl or shell) has exited, it is necessary for the parent shell to do the heavy lifting. One method I've seen in the documentation is for the current script to spawn an executable file with the necessary 'export' language, and then trick the parent shell into executing it -- always being cognizant of the fact that you need to preface the command with 'source' if you're trying to leave a non-volatile version of the modified environment behind. A Kluge at best.

如果环境变量在父(Perl或shell)退出后仍然是绝对必要的,那么父shell就必须执行繁重的任务。文档我看过的一个方法是对当前脚本生成一个可执行文件进行必要的“出口”语言,然后诱骗父shell执行它,总是被认识到的事实需要序言命令与“源”如果你想留下的非易失性版本修改的环境。一个拼凑起来的。

The second method is to modify the script that initiates the shell environment (.bashrc or whatever) to contain the modified parameter. This can be dangerous -- if you hose up the initialization script it may make your shell unavailable the next time it tries to launch. There are plenty of tools for modifying the current shell; by affixing the necessary tweaks to the 'launcher' you effectively push those changes forward as well. Generally not a good idea; if you only need the environment changes for a particular application suite, you'll have to go back and return the shell launch script to its pristine state (using vi or whatever) afterwards.

第二个方法是修改启动shell环境的脚本(。bashrc或其他)以包含修改后的参数。这可能是危险的——如果您将初始化脚本连接起来,它可能会使您的shell在下一次尝试启动时无法使用。有很多工具可以修改当前的shell;通过对“启动器”进行必要的调整,你可以有效地推动这些变化。通常不是个好主意;如果您只需要对特定应用程序套件进行环境更改,那么您必须返回并将shell启动脚本返回到其原始状态(使用vi或其他)。

In short, there are no good (and easy) methods. Presumably this was made difficult to ensure the security of the system was not irrevocably compromised.

简而言之,没有好的(简单的)方法。这可能使确保系统的安全性不受不可挽回的损害变得困难。

#13


1  

The short answer is no, you cannot alter the environment of the parent process, but it seems like what you want is an environment with custom environment variables and the shell that the user has chosen.

简短的回答是不,您不能更改父进程的环境,但是看起来您想要的是一个具有自定义环境变量和用户选择的外壳的环境。

So why not simply something like

所以为什么不简单地像

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

Then when you are done with the environment, just exit.

然后,当您处理完环境后,只需退出。

#14


1  

You could always use aliases

你可以使用别名

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

#15


1  

Another option is to use "Environment Modules" (http://modules.sourceforge.net/). This unfortunately introduces a third language into the mix. You define the environment with the language of Tcl, but there are a few handy commands for typical modifications (prepend vs. append vs set). You will also need to have environment modules installed. You can then use module load *XXX* to name the environment you want. The module command is basically a fancy alias for the eval mechanism described above by Thomas Kammeyer. The main advantage here is that you can maintain the environment in one language and rely on "Environment Modules" to translate it to sh, ksh, bash, csh, tcsh, zsh, python (?!?!!), etc.

另一种选择是使用“环境模块”(http://modules.sourceforge.net/)。这不幸地引入了第三种语言。您使用Tcl语言定义环境,但是有一些用于典型修改的方便命令(prepend vs. append vs. set)。您还需要安装环境模块。然后可以使用模块load *XXX*来命名所需的环境。模块命令基本上是上面Thomas Kammeyer描述的eval机制的花哨别名。这里的主要优点是,您可以使用一种语言维护环境,并依赖“环境模块”将其转换为sh、ksh、bash、csh、tcsh、zsh、python(?! !!)等。

#16


1  

I did this many years ago. If I rememeber correctly, I included an alias in each of .bashrc and .cshrc, with parameters, aliasing the respective forms of setting the environment to a common form.

这是我多年前做的。如果我没记错的话,我在每个.bashrc和.cshrc中都包含了一个别名,其中包含了参数,以使将环境设置为公共形式的相应形式具有别名。

Then the script that you will source in any of the two shells has a command with that last form, that is suitable aliased in each shell.

然后,您将在任何两个shell中提供的脚本都有一个与最后一个表单相匹配的命令,该命令在每个shell中都是合适的。

If I find the concrete aliases, I will post them.

如果我找到具体的别名,我会贴上去。

#17


1  

I created a solution using pipes, eval and signal.

我使用管道、eval和signal创建了一个解决方案。

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

It might work with any command.

它可以使用任何命令。

#18


0  

Under OS X bash you can do the following:
Create the bash script file to unset the variable

在OS X bash下,您可以执行以下操作:创建bash脚本文件来取消变量的设置

#!/bin/bash
unset http_proxy

Make the file executable

使文件可执行

sudo chmod 744 unsetvar

Create alias

创建别名

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

It should be ready to use so long you have the folder containing your script file appended to the path.

它应该准备好使用这么长时间,您将包含脚本文件的文件夹附加到路径。

#19


0  

I don't see any answer documenting how to work around this problem with cooperating processes. A common pattern with things like ssh-agent is to have the child process print an expression which the parent can eval.

我没有看到任何关于如何使用协作过程来解决这个问题的答案。与ssh-agent类似的常见模式是让子进程打印父进程可以计算的表达式。

bash$ eval $(shh-agent)

For example, ssh-agent has options to select Csh or Bourne-compatible output syntax.

例如,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;

(This causes the agent to start running, but doesn't allow you to actually use it, unless you now copy-paste this output to your shell prompt.) Compare:

(这会导致代理开始运行,但不允许实际使用它,除非您现在将该输出复制到shell提示符。)比较:

bash$ ssh-agent -c
setenv SSH2_AUTH_SOCK /tmp/ssh-era/ssh2-10751-agent;
setenv SSH2_AGENT_PID 10752;
echo Agent pid 10752;

(As you can see, csh and tcsh uses setenv to set varibles.)

(如您所见,csh和tcsh使用setenv设置变量。)

Your own program can do this, too.

你自己的程序也可以做到这一点。

bash$ foo=$(makefoo)

Your makefoo script would simply calculate and print the value, and let the caller do whatever they want with it -- assigning it to a variable is a common use case, but probably not something you want to hard-code into the tool which produces the value.

您的makefoo脚本只需计算并打印值,并让调用者使用它做他们想做的任何事情——将它分配给一个变量是一个常见的用例,但可能不是您想硬编码到生成值的工具中的东西。

#20


-9  

Other than writings conditionals depending on what $SHELL/$TERM is set to, no. What's wrong with using Perl? It's pretty ubiquitous (I can't think of a single UNIX variant that doesn't have it), and it'll spare you the trouble.

除了根据$SHELL/$TERM设置的写作条件外,没有。使用Perl有什么问题?它是非常普遍的(我想不出一个没有它的UNIX变体),并且它将为您省去麻烦。