2007年7月20日

想想挺容易:用脚本改变当前 shell 的环境变量

前言

世上有些事就是这样的,想想挺容易,下手做的时候才发现问题多多
所以,学技术活呀,千万不能眼高手低
明明已经成功N次的操作,可能就会在第N+1次难倒你!!
这不,差点撞到流血。。。。。


引子

---引自我在 Ubuntu China 的发贴

不用 . 或 source 还有没有别的办法修改当前环境变量??

当前环境变量里 a=kkkkkkkk,运行脚本 export a=sssssss ,在脚本运行过程中(fork)变量a是被改了,可是子进程结束 后,当前环境变显的a没有受到影响。

bash里有没有类似C的setenv函数?

---难题说破了就不难了,继续往下看之前想想你有什么高招??


背景

在 Linux 下面改环境变量也不是第一次了,通常是在命令行下面输入: export ORACLE_SID=test ,即刻生效。

如果要长期生效,自然就是加到 .bashrc 或 .profile 里面去,终端启动时会自动加载

如果修改了 .bashrc 或 .profile 文件, 用: . <点号> 把文件读出来执行一遍就行了,如:
. $HOME/.bashrc

其它的还有如,bash的 env、export,ksh的setenv、source,C语言的getenv,setenv等等。


问题

前几天遇到一个这样的问题:
当前环境变量存放了 CVSROOT 的信息:
CVSROOT=:pserver:USER1@host:/cvsdir
当多个客户以 USER1 登录到系统以后,要各自使用自已的用户名操作 CVS

脚本如下:
-------------- mycvs -----------------
#!/bin/bash
printf "%s" " enter your cvs account : "
read user_name

cvs logout
unset CVSROOT

export CVSROOT=":pserver:${user_name}@host:/cvsdir"
cvs login
------------------------------------------

分析

其实是个为了简化操作的脚本,思路很简单,就是重置一下环境变量,然后调用 cvs login 重新登录。

结果呢?问题来了。。。。。这个脚本不能直接运行。

如果在控制台输入 mycvs 运行,当时以新用户名(CVSROOT=":pserver:myname@host:/cvsdir)登录了,但是登录之后的操作,如 cvs commit 等,仍然使用的老环境变量CVSROOT=:pserver:USER1@host:/cvsdir。

!!!!无法更改环境变量!!!!


( 现在回想起来是非常明显的错误,当时还是迷糊了半天 )


我们知道,SHELL 执行一个脚本时是 fork() 一个子进程,所以脚本运行时,都在一个新的环境里面。用 unset 或 export 修改的环境变量其实是一件“复制品”。等到脚本运行完毕,当前 SHELL 的环境变量压跟就不受任何影响。

开始的时候,还在幻想用 C 语言重写这个脚本,因为 C 语言的 setenv() 是直接对内存指针操作的,SHELL 没有能力更改环境变量是因为不能突破 ENV 的限制,但是 C 能够。
现在想想,即始用 C 语言能够实现我所要的效果,也绝不是简单的用 setenv() 就能解决的,因为 setenv() 也是使用的一份“复制品”的环境变量,这些都没逃出 FORK 的圈套。

后来有人提醒我,去掉脚本的Magic标识部分,即不要 #!/bin/bash的部分。这的确令我眼前一亮,痛骂自已怎么忽略了这一点。
我们知道,如果文件是 #!/bin/bash 开头,BASH会将其识别成 BournShell ,但是如果没有Magic头,就会只作为一般的命令行集执行,这两者是有区别的。
但是仍然不行。

(说个题外话,用C语言能通过内存扫描找到并修改环境变量,实现我想要的效果吗?这中间会涉及到安全因素吗?)



结果(两天以后)

找到一个变通的办法, 思路如下:

先写一个脚本,内容是
----------test.sh ------------
read name
export MYNAME=$name

然后在 .bashrc 里定义
alias chgname=". test.sh"


-----this is a very dirty way, 求其它解法 ----

没有评论: