我正在尝试编写一个shell脚本,在运行时,将设置一些将在调用者的shell中保留的环境变量.
setenv FOO foo
在csh/tcsh中,或
export FOO=foo
在sh/bash中只在脚本执行期间设置它.
我已经知道了
source myscript
将运行脚本的命令而不是启动新的shell,这可能导致设置"调用者"环境.
但这里有一个问题:
我希望这个脚本可以从bash或csh调用.换句话说,我希望任何一个shell的用户都能够运行我的脚本并改变他们的shell环境.所以'source'对我来说不起作用,因为运行csh的用户无法获取bash脚本,而运行bash的用户无法获取csh脚本.
有没有合理的解决方案不需要在脚本上编写和维护两个版本?
使用"点空间脚本"调用语法.例如,以下是如何使用脚本的完整路径来完成它:
. /path/to/set_env_vars.sh
如果您与脚本位于同一目录中,请执行以下操作:
. set_env_vars.sh
它们在当前shell下执行脚本而不是加载另一个脚本(如果你这样做会发生这种情况./set_env_vars.sh
).因为它在同一个shell中运行,所以您设置的环境变量在退出时将可用.
这与调用相同source set_env_vars.sh
,但是键入的时间更短,并且可能在某些source
没有调用的地方工作.
您的shell进程具有父级环境的副本,并且无法访问父进程的环境.当shell进程终止时,您对其环境所做的任何更改都将丢失.获取脚本文件是配置shell环境最常用的方法,您可能只想咬住子弹并为两种shell中的每一种维护一个.
您无法修改调用者的shell,因为它位于不同的进程上下文中.当子进程继承shell的变量时,它们自己继承副本.
您可以做的一件事是编写一个脚本,根据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只是用你喜欢的shell编写,如:
#!/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
使用上面给出的符号链接和反引号表达式的eval,这具有期望的结果.
要简化csh,tcsh或类似shell的调用:
alias dosetit 'eval `setit-csh`'
或者sh,bash等:
alias dosetit='eval `setit-sh`'
关于这一点的一个好处是你只需要将列表保存在一个地方.从理论上讲,你甚至可以将列表放在一个文件中,放在cat nvpairfilename
"in"和"do"之间.
这几乎是如何进行登录shell终端设置的:脚本将输出要在登录shell中执行的语句.别名通常用于简化调用,如"tset vt100".正如另一个答案中所提到的,INN UseNet新闻服务器中也有类似的功能.
在我的.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
}
因此,当我想要禁用代理时,函数会在登录shell中运行,并按预期和需要设置变量.
通过使用gdb和setenv(3)可以"有点" ,虽然我很难推荐实际做到这一点.(另外,即最近的ubuntu实际上不会让你这样做而不告诉内核对ptrace更加宽容,同样可能也适用于其他发行版).
$ cat setfoo #! /bin/bash gdb /proc/${PPID}/exe ${PPID} </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的解释,出口环境变量,然后替换自己与新的交互式shell.
运行此脚本之前,我们已经SHELL
在环境C shell的设置,以及环境变量TEREDO_WORMS
未设置:
% env | grep SHELL SHELL=/bin/csh % env | grep TEREDO %
当运行脚本,你是在一个新的shell,另一个交互式的C shell,但环境变量设置:
% teredo % env | grep TEREDO TEREDO_WORMS=ukelele %
当您从此shell退出时,原始shell将接管:
% exit % env | grep TEREDO %
环境变量未在原始shell的环境中设置.如果您使用exec teredo
命令运行,则原始交互式shell将由设置环境的Korn shell替换,然后由新的交互式C shell替换:
% exec teredo % env | grep TEREDO TEREDO_WORMS=ukelele %
如果你键入exit
(或Control-D),那么你的shell会退出,可能会把你从那个窗口退出,或者带你回到实验开始的前一层shell.
相同的机制适用于Bash或Korn shell.您可能会发现退出命令后的提示出现在有趣的地方.
请注意评论中的讨论.这不是我建议的解决方案,但它确实实现了单个脚本的声明目的,即设置适用于所有shell的环境(接受-i
创建交互式shell 的选项).您也可以"$@"
在选项后添加中继任何其他参数,这可能使shell可用作一般的"设置环境和执行命令"工具.您可能想要省略-i
if是否有其他参数,导致:
#!/bin/ksh export TEREDO_WORMS=ukelele exec $SHELL "${@-'-i'}"
该"${@-'-i'}"
位表示'如果参数列表包含至少一个参数,则使用原始参数列表; 否则,替换-i
不存在的参数'.
您应该使用模块,请参阅http://modules.sourceforge.net/
编辑:自2012年以来,模块包尚未更新,但仍然适用于基础知识.所有新功能,铃声和口哨都发生在今天的lmod中(我更喜欢它):https://www.tacc.utexas.edu/research-development/tacc-projects/lmod
我没有看到的另一个解决方法是将变量值写入文件.
我遇到了一个非常类似的问题,我想能够运行最后一次设置测试(而不是我所有的测试).我的第一个计划是编写一个用于设置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