有没有办法改变Unix中另一个进程的环境变量?


105

在Unix上,是否有任何方法可以改变一个进程的环境变量(假设它们都由同一用户运行)?通用的解决方案是最好的,但是如果不是,那么一个孩子是另一个孩子的特殊情况又如何呢?

编辑:如何通过gdb?


这让我印象深刻。您要解决的实际问题是什么?
詹斯(Jens)2012年

1
示例:我想定义一个环境变量,以便由UI启动的每个新应用程序都可以获取它。除了在启动脚本之一和RE-LOGIN中定义变量外,我不知道任何方法。但是,我不想重新登录,而只是在当前会话中定义变量,以便新应用程序可以获取它-无需注销UI。
AlikElzin-kilaka 2012年

Answers:


142

通过gdb:

(gdb) attach process_id

(gdb) call putenv ("env_var_name=env_var_value")

(gdb) detach

当然,这是一个令人讨厌的技巧,仅应在调试场景的情况下进行。


8
因此,这似乎意味着,如果像GDB那样附加到流程,然后分离,则确实可以更改流程的环境。似乎有可能编写仅执行此操作的程序。
悲伤

3
“看来有可能编写仅执行此操作的程序”确实如此。
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳ 2010年

2
它甚至适用于使用cygwin的Windows,适用于未使用cygwin编译的进程!
胡安·卡洛斯·穆尼兹

11
请注意,这仅在进程尚未在先前的getenv之后永久缓存该值时才有效。
An̲̳̳drew

1
ptrace: Operation not permitted
格里特2014年

22

您可能可以从技术上做到这一点(请参阅其他答案),但这可能无济于事。

大多数程序会期望启动后无法从外部更改env var,因此大多数程序可能只会读取启动时对其感兴趣的var,并基于此进行初始化。因此,事后更改它们不会造成任何影响,因为该程序永远不会重新读取它们。

如果您将此发布为具体问题,则可能应该采用其他方法。如果只是出于好奇:好问题:-)。


1
最有用的用例是使子进程继承新的环境变量,例如,在桌面环境中,您希望新的终端使用新的环境变量。
Hjulle

13

基本上没有。如果您具有足够的特权(root或大约特权)并在/ dev / kmem(内核内存)中戳,并且您对进程的环境进行了更改,并且该进程之后是否实际重新引用了环境变量(即该进程)尚未制作过env var的副本,并且不只是使用该副本),也许,如果您很幸运和聪明,风向正确的方向吹来,月亮的相位是正确的,也许,你可能会有所成就。


2
我没有得到答案。
AlikElzin-kilaka 2012年

@kilaka:关键字是第二个关键字- 。答案的其余部分是说,如果你有root权限或正在运行调试器,那么也许你可以做到这一点,但对于所有的实际目的,答案是没有
乔纳森·莱夫勒

您已经运行了一个shell脚本;您想在shell脚本的父进程中更改环境...因此,shell脚本gdb在父进程上启动并被脚本化为可以进行更改,并且该脚本可以在不崩溃父进程的情况下工作。好的-您可能可以做到,但这不是您日常要做的事情。因此,出于实际目的,答案仍然是“ 否”。其余的答案涵盖了理论上可行的替代方案,有些可行的替代方案。
乔纳森·勒夫勒

7

引用杰里·皮克(Jerry Peek):

你不能教老狗新的花样。

您唯一可以做的就是在更改子进程的环境变量之前启动:抱歉,它获取了父环境的副本。

有关详细信息,请参见http://www.unix.com.ua/orelly/unix/upt/ch06_02.htm

只是对有关使用/ proc的答案的评论。在linux下,支持/ proc,但它不起作用,即使您是root用户,也无法更改/proc/${pid}/environ文件:它绝对是只读的。


还有什么问题:env var值实际存储在哪里?那是由内核完成的吗?还是shell存储值,然后/ proc / <pid> / environ从那里获取值?
奥利弗

这是一个实现细节,可能是一个(单独的)好问题。我认为每个UNIX都使用自己的存储方式,但是它们都具有上述行为,这是规范的一部分。
Davide

7

我可以想到这样做的相当人为的方法,它不适用于任意过程。

假设您编写了自己的实现“ char * getenv”的共享库。然后,您设置“ LD_PRELOAD”或“ LD_LIBRARY_PATH”环境。vars,这样您的两个进程都将在预加载共享库的情况下运行。

这样,您将基本上可以控制“ getenv”函数的代码。然后,您可以进行各种讨厌的把戏。您的“ getenv”可以查询外部配置文件或SHM段以获取env vars的备用值。或者,您可以对请求的值进行正则表达式搜索/替换。要么 ...

我想不出一种简单的方法来对任意正在运行的进程(即使您是root用户)执行此操作,而无需重写动态链接程序(ld-linux.so)。


这应该可行。您可能有一个用于var = value对的gdbm数据库。我在stromberg.dnsalias.org/~strombrg/malloc-wrapper
dstromberg

我认为这种方法需要预先考虑。您还必须注意不要将其意外地应用于过多的过程。
dstromberg '19

3

或让您的进程为新进程更新配置文件,然后执行以下任一操作:

  • 对新进程执行kill -HUP以重新读取更新的配置文件,或者
  • 让进程不时检查配置文件是否有更新。如果找到更改,则重新读取配置文件。

2

据我所知。确实,您正在尝试从一个进程与另一个进程进行通信,该进程需要一种IPC方法(共享内存,信号量,套接字等)。通过这些方法之一接收到数据后,您便可以设置环境变量或更直接地执行其他操作。


1

如果您的Unix支持/ proc文件系统,那么读取环境很简单-您可以以这种方式读取任何进程的环境,命令行和许多其他属性。更改它...好吧,我可以想到一种方法,但这是一个坏主意。

更一般的情况...我不知道,但我怀疑是否有一个可移植的答案。

(编辑:我的原始答案假设OP想要读取环境,而不是更改它)


糟糕,编辑了我的答案-我以为他想阅读环境,而不是更改它。
Mike G.

1
不要让我挂着。你有什么不好的主意?
raldi

在Linux上,我相信您可以为您拥有的其他进程打开/ proc / <pid> / mem读写功能,但是我不确定。尝试并实际上与环境打交道,绝对不是一个好主意。所以我不建议您尝试...
Mike G.

1

UNIX充满了进程间通信。检查您的目标实例是否有对象。Dbus正在成为“桌面” IPC的标准。

我在awesome窗口管理器中使用awesome-client更改了环境变量,该客户端是lua代码的Dbus“发送者”。


1

不是直接的答案,而是…… Raymond Chen前几天对此有[基于Windows]的基本原理:-

...尽管肯定有不受支持的执行方法或在调试器的协助下工作的方法,但是对以编程方式访问另一个进程的命令行不提供任何支持,至少内核不提供任何支持。...

不存在是由于不跟踪不需要的信息这一原则的结果。内核无需获取其他进程的命令行。它接受传递给该CreateProcess函数的命令行,并将其复制到正在启动的进程的地址空间中,该位置GetCommandLine可以找到该函数。一旦进程可以访问其自己的命令行,就完成了内核的职责。

由于命令行已复制到进程的地址空间中,因此进程甚至可能会写入保存命令行的内存并对其进行修改。如果发生这种情况,那么原始命令行将永远丢失;唯一已知的副本被覆盖。

换句话说,任何这样的内核功能都是

  • 难以实施
  • 潜在的安全隐患

但是,最可能的原因仅仅是这种工具的用例有限。


1

看来putenv现在不起作用,但是setenv可以。我在尝试在当前外壳程序中设置变量时测试了可接受的答案,但没有成功

$] sudo gdb -p $$
(gdb) call putenv("TEST=1234")
$1 = 0
(gdb) call (char*) getenv("TEST")
$2 = 0x0
(gdb) detach
(gdb) quit
$] echo "TEST=$TEST"
TEST=

以及变体的工作方式:

$] sudo gdb -p $$
(gdb) call (int) setenv("TEST", "1234", 1)
$1 = 0
(gdb) call (char*) getenv("TEST")
$2 = 0x55f19ff5edc0 "1234"
(gdb) detach
(gdb) quit
$] echo "TEST=$TEST"
TEST=1234
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.