如何在Bash中源一个CSH脚本以设置环境变量

23

我们在Solaris上运行Oracle, 默认使用csh作为shell。登录脚本在csh中设置oracle_home、oracle_sid等变量。但是我不喜欢csh,想要使用bash来完成工作。那么如何在bash中引用csh的登录脚本呢?

例如,以下是.cshrc文件中的内容。当使用bash时,我希望使用这些变量。一种方法是再次复制变量并使用bash命令,例如export ORACLE_SID=TEST。但这样做需要我们维护两份文件。当我们更改数据库名称或升级数据库时,我需要单独维护bash登录文件。最好的方法是在bash中使用source .cshr,但它不起作用。

setenv ORACLE_SID TEST
setenv ORACLE_HOME /oracle/TEST/home/products/10204
setenv EPC_DISABLED TRUE
setenv MANPATH /usr/local/man:/usr/share/man
setenv EDITOR vi
setenv LD_LIBRARY_PATH $ORACLE_HOME/lib:/usr/sfw/lib/64
setenv NLS_LANG AMERICAN_AMERICA.UTF8
setenv NLS_DATE_FORMAT "DD-MON-RR"

1
适合Super User还是Server Fault? - sblom
9个回答

20
在你的~/.bashrc文件中(或者是~/.bash_profile~/.bash_login~/.profile中存在的第一个文件),使用类似于. ~/bin/sourcecsh的命令来引用这个脚本。
#!/bin/bash
# This should be sourced rather than executed
while read cmd var val
do
    if [[ $cmd == "setenv" ]]
    then
        eval "export $var=$val"
    fi
done < ~/.cshrc

这个版本消除了邪恶的eval函数:

#!/bin/bash
# This should be sourced rather than executed
# yes, it will be sourcing within sourcing - what so(u)rcery!
source /dev/stdin < \
<(
    while read cmd var val
    do
        if [[ $cmd == "setenv" ]]
        then
             echo "export $var=$val"
        fi
    done < cshrc
)

编辑:

不需要从stdin中获取来源:

while read cmd var val
do
    if [[ $cmd == "setenv" ]]
    then
        declare -x "$var=$val"
    fi
done < cshrc

@Dennis - 不错...我喜欢这个,因为它是纯Bash(我的使用了sed)...我必须承认,我仍然更喜欢我的版本(相当相似),主要是因为它将设置命令列表保存为文件以备后续故障排除或环境恢复之用 :) - DVK
1
我会投票支持第一个版本--从标准输入中获取数据与“eval”一样容易出现问题。 - Gordon Davisson

7

我们可以定义一个名为 setenv 的函数,如下所示:

setenv() {
  echo setting $1 to $2
  export $1=$2
}

然后找到.cshrc文件。

当我在bash中执行此操作时,会得到以下结果:

[dws@oxygen ual-read-only]$ source cshrc
setting ORACLE_SID to TEST
setting ORACLE_HOME to /oracle/TEST/home/products/10204
setting EPC_DISABLED to TRUE
setting MANPATH to /usr/local/man:/usr/share/man
setting EDITOR to vi
setting LD_LIBRARY_PATH to /oracle/TEST/home/products/10204/lib:/usr/sfw/lib/64
setting NLS_LANG to AMERICAN_AMERICA.UTF8
setting NLS_DATE_FORMAT to DD-MON-RR
[dws@oxygen ual-read-only]$ env | grep ORACLE
ORACLE_SID=TEST
ORACLE_HOME=/oracle/TEST/home/products/10204

差强人意。 (1) 一些csh源脚本设置别名 - 我不知道如何将别名从csh导出回调用shell,例如bash。我想这只是一个简单的编程问题,将任何csh别名转换为bash别名,反之亦然(后者用于调用新的csh源脚本)。 (2) 由于我的正常登录shell有额外的环境变量,所以我需要先清空它。这就是我要问的。 (3) 哦,刚刚注意到你正在做什么:是的,我多年来一直在bash中使用setenv。 (实际上,在bash存在之前,我就在ksh中使用了它。) - Krazy Glew

4

我在同样的困境中。一位同事向我展示了以下内容:

在bash中开始,不包括环境中的任何内容:

bash> echo $$
12632
bash> echo $FOO

这是被引用的csh文件:

bash> cat setup-env.csh
setenv FOO "some csh stuff"
echo FOO=$FOO in csh

这是命令:

bash> csh -c 'source setup-env.csh;exec bash'

请查看csh的输出。
FOO=some csh stuff in csh

看一下新bash shell的输出

  bash> echo $$
  13487
  bash> echo $FOO
  some csh stuff

现在退出,返回原始的 bash shell。
bash> exit
exit
bash> echo $$
12632
bash> 

请注意使用echo $$命令查看进程ID,以便我们可以看到它们是不同的shell进程。

我的同事经常使用这个命令并将其放入别名中,例如:

# make csh environment scripts useable (sourceable) from bash function 
# from Phil McCoy, Wed Nov  9 2011
source_csh () {
   exec csh -c " source $*; setenv ALREADY_SOURCED \"$ALREADY_SOURCED:$*:\"; exec bash"
}
# sounds like a great idea to do source_csh .cshrc or .login
# but naively done is infinitely recursive, 
# since the exec'ed bash will run .bashrc

很不幸,我发现不仅需要设置环境变量,还需要设置别名,就像在modules package中一样。

我已经能够通过使用Perl Expect自动化这个“csh source script recipes”的过程。但是除了上面的方法,我还没有能够像我想要的那样进行交互。


我是模块包的忠实粉丝,我很惊讶为什么没有更多的人使用它。使事情与Shell无关只是一件好事。 - stephenmm

2
在你的bash .profile 中,你可以执行以下操作:
cat .cshrc | sed 's/setenv\s+(\S+)\s+(.*)$/set $1=$2; export $1/' > $HOME/.env_from_csh
source $HOME/.env_from_csh

这也很酷,我更喜欢不创建一些垃圾文件。不确定是否可以使用管道来避免临时文件。 - Daniel
1
“cat” 的使用是无用的。Bash 不会以那种方式使用 “set”。 “export” 可以一次性接受变量名称和赋值。你必须使用不同版本的 “sed”,因为那看起来像 Perl 语法。你需要使用 “-r” 或转义括号和加号。你可以像这样消除临时文件:source /dev/stdin < <(sed 's/setenv\s\+\(\S\+\)\s\+\(.*\)$/export \1=\2/' .cshrc) - Dennis Williamson
@Dennis 和 Daniel - 我通常需要那个“垃圾”临时文件 :) - DVK
2
source /dev/stdin < <(sed 's/setenv\s\+\(\S\+\)\s\+\(.*\)$/export \1=\2/' .cshrc | tee $HOME/.env_from_csh) - Dennis Williamson

2

我能想到的唯一方法是加载csh,然后从新的shell中调用bash。这样,csh可以解析该文件,然后生成的bash也将继承该环境。


在代码中:(cat .cshrc; echo exec $SHELL) | csh - reinierpost

0

有一个模块可用,您可以在perl脚本中的任何位置获取相同的文件。您将获得csh文件中所有可用的环境路径。

Source::Shell

阅读一些有关其使用的文档。


0

对于这么小的东西,通常会维护两个设置脚本,一个用于 sh 和 sh 派生的 shell,另一个用于 csh 和 tcsh。正如您所提到的,这确实会导致两个脚本不同步的风险,除非您从其中一个生成另一个,或者从公共源生成两个。

这将使设置脚本的维护者承担负担,而不是每个需要使用它们的用户。


-1

只需在CSH脚本开头设置“#!/bin/tcsh”或类似语句,并使脚本可执行即可解决我的问题。在这种情况下,我可以直接从bash调用CSH脚本。

例如,我必须从名为setup.sh的bash脚本中运行tools.csh。在bash脚本中,我做了类似于以下操作:

if [ -z \`head -1 tools.csh | grep '^#!'\` ];
then
   TCSH=\`which tcsh\`;
   echo "'#!'$TCSH" > tools.csh.temp;
   cat tools.csh >> tools.csh.temp;
   mv tools.csh.temp tools.csh;
fi;
chmod 755 tools.csh;
./tools.csh
# now I have all the setenv commands effective ...

1
如果 ./tools.csh 设置了环境变量,则这些变量不会在调用 shell 中可见。如果依赖这些环境变量的命令都在 ./tools.csh 中,那就没问题。 - Keith Thompson

-4
在bash中引用csh文件是行不通的。如果你更喜欢使用bash,可以将默认登录shell从csh更改为bash。你可以使用chsh或者作为管理员来为你进行更改。
chsh -s /usr/local/bin/bash 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接