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

setenv FOO foo

在csh/tcsh中,或

export FOO=foo

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

我已经知道了

source myscript

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

但问题是:

我希望这个脚本可以从bash或csh中调用。换句话说,我希望任何一个shell的用户都能够运行我的脚本,并更改他们的shell环境。因此,'source'对我来说不适用,因为运行csh的用户不能获取bash脚本的源代码,而运行bash的用户也不能获取csh脚本的源代码。

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


当前回答

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

其他回答

这是有效的-这不是我要用的,但它“有效”。让我们创建一个脚本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
%

当你退出这个外壳时,原来的外壳接管:

% 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,导致:

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

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

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

#!/bin/bash
unset http_proxy

使文件可执行

sudo chmod 744 unsetvar

创建别名

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

只要将包含脚本文件的文件夹添加到路径中,它就可以使用了。

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

你可以使用别名

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

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