有没有办法改变 Unix 中另一个进程的环境变量?
Posted
技术标签:
【中文标题】有没有办法改变 Unix 中另一个进程的环境变量?【英文标题】:Is there a way to change the environment variables of another process in Unix? 【发布时间】:2010-09-17 07:57:14 【问题描述】:在 Unix 上,有什么方法可以让一个进程更改另一个进程的环境变量(假设它们都由同一个用户运行)?一般的解决方案是最好的,但如果不是,那一个是另一个孩子的具体情况呢?
编辑:通过 gdb 怎么样?
【问题讨论】:
这给我的印象不仅仅是丑陋。您要解决的实际问题是什么? 示例:我想定义一个环境变量,以便每个由 UI 启动的新应用程序都能获得它。除了在启动脚本之一和重新登录中定义变量外,我不知道任何方法。但是,我不想重新登录,而只是在当前会话中定义变量,以便新应用程序可以获取它 - 而无需退出 UI。 【参考方案1】:通过 gdb:
(gdb) attach process_id
(gdb) call putenv ("env_var_name=env_var_value")
(gdb) detach
这是一个非常讨厌的 hack,当然只能在调试场景的上下文中完成。
【讨论】:
所以这似乎暗示你确实可以改变一个进程的环境,如果你像 GDB 那样附加到进程,然后分离。似乎可以编写一个仅执行此操作的程序。 “似乎可以编写一个只做这个的程序”确实......确实如此。 它甚至可以在使用 cygwin 的 Windows 上运行,用于未使用 cygwin 编译的进程! 请注意,这仅适用于进程在之前的 getenv 之后没有永久缓存该值的情况。 在某些系统上 gdb 可能会给出以下错误:'putenv' has unknown return type; cast the call to its declared return type
;在这些情况下,您应该将putenv
调用更改为:call (int) putenv ("env_var_name=env_var_value")
【参考方案2】:
您可能可以在技术上做到这一点(请参阅其他答案),但它可能对您没有帮助。
大多数程序会期望环境变量在启动后不能从外部更改,因此大多数程序可能只会在启动时读取他们感兴趣的变量并基于此进行初始化。因此,之后更改它们不会有任何影响,因为程序永远不会重新读取它们。
如果您将此作为具体问题发布,您可能应该采取不同的方法。如果只是出于好奇:好问题:-)。
【讨论】:
最常见的用例是让子进程继承新的环境变量,例如,在您希望新终端使用新变量的桌面环境中。【参考方案3】:基本上,没有。如果您有足够的权限(root 或类似权限)并在 /dev/kmem(内核内存)附近进行了操作,并且您对进程的环境进行了更改,并且该进程实际上在之后重新引用了环境变量(即进程尚未获取 env var 的副本并且不只使用该副本),那么也许,如果你幸运且聪明,并且风向正确的方向吹,并且月相是正确的,也许,你可能会有所成就。
【讨论】:
@kilaka:关键词是第二个——否。其余的答案是说,如果您有 root 权限或正在运行调试器,那么也许您可以这样做,但出于所有实际目的,答案是 否。 你已经运行了一个 shell 脚本;您想更改 shell 脚本的父进程中的环境...因此 shell 脚本在父进程上启动gdb
并编写脚本来进行更改,并且它可以在不使父进程崩溃的情况下工作。好的——你可能可以做到,但这不是你日常要做的事情。因此,出于实际目的,答案仍然是否。其余的答案涵盖了理论上可能的、有些不切实际的可行替代方案。【参考方案4】:
引用杰瑞·皮克的话:
你不能教老狗新把戏。
你唯一能做的就是改变子进程的环境变量在启动它:它得到父环境的副本,对不起。
详情请见http://www.unix.com.ua/orelly/unix/upt/ch06_02.htm。
只是对有关使用 /proc 的答案的评论。在 linux 下 /proc 受支持,但它不起作用,您不能更改 /proc/$pid/environ
文件,即使您是 root:它绝对是只读的。
【讨论】:
还有一个问题:env var 值实际存储在哪里?这是由内核完成的吗?还是 shell 存储这些值,然后 /proc/我可以想到一种相当人为的方法来做到这一点,它不适用于任意进程。
假设您编写了自己的共享库,该库实现了“char *getenv”。然后,您设置“LD_PRELOAD”或“LD_LIBRARY_PATH”环境。 vars 以便您的两个进程都在预加载共享库的情况下运行。
这样,您基本上可以控制“getenv”函数的代码。然后,你可以做各种讨厌的把戏。您的“getenv”可以咨询外部配置文件或 SHM 段以获取 env vars 的替代值。或者您可以对请求的值进行正则表达式搜索/替换。或者……
除了重写动态链接器 (ld-linux.so) 之外,我想不出一种简单的方法来为任意运行的进程(即使你是 root)做到这一点。
【讨论】:
这应该是可行的。您可以为 var=value 对创建一个小的 gdbm 数据库。我在 stromberg.dnsalias.org/~strombrg/malloc-wrapper 有类似的 malloc 我认为这种方法需要深思熟虑。您还必须小心不要意外地将其应用于太多进程。【参考方案6】:或者让您的进程为新进程更新配置文件,然后:
对新进程执行 kill -HUP 以重新读取更新的配置文件,或者 让进程不时检查配置文件的更新。如果发现更改,则重新读取配置文件。【讨论】:
【参考方案7】:现在看来 putenv 不起作用,但 setenv 可以。 我正在测试接受的答案,同时尝试在当前 shell 中设置变量但没有成功
$] 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
【讨论】:
【参考方案8】:据我所知没有。实际上,您正在尝试从一个进程与另一个进程进行通信,这需要一种 IPC 方法(共享内存、信号量、套接字等)。通过其中一种方法接收数据后,您可以设置环境变量或更直接地执行其他操作。
【讨论】:
【参考方案9】:如果您的 unix 支持 /proc 文件系统,那么读取 env 就很简单了——您可以通过这种方式读取环境、命令行和您拥有的任何进程的许多其他属性。改变它...好吧,我可以想办法,但这是一个坏主意。
更一般的情况...我不知道,但我怀疑是否有可移植的答案。
(已编辑:我的原始答案假设 OP 想要读取环境,而不是更改它)
【讨论】:
哎呀,编辑了我的答案 - 我假设他想阅读环境,而不是改变它。 别让我挂了。你有什么坏主意? 在 Linux 上,我相信您可能能够为您拥有的其他进程打开 /proc/UNIX 充满了进程间通信。检查您的目标实例是否有一些。 Dbus 正在成为“桌面”IPC 的标准。
我使用 awesome-client 在 Awesome 窗口管理器中更改环境变量,它是 lua 代码的 Dbus“发送者”。
【讨论】:
【参考方案11】:不是直接的答案,但是...Raymond Chen had a [Windows-based] rationale around this only the other day :-
...虽然肯定有一些不受支持的方法或在调试器的帮助下工作的方法,但没有任何东西支持以编程方式访问另一个进程的命令行,至少内核没有提供任何东西。 ...
不跟踪您不需要的信息这一原则的结果。内核不需要获取另一个进程的命令行。它将命令行传递给
CreateProcess
函数并将其复制到正在启动的进程的地址空间中,在GetCommandLine
函数可以检索它的位置。一旦进程可以访问自己的命令行,内核的职责就完成了。由于命令行被复制到进程的地址空间,进程甚至可能写入保存命令行的内存并修改它。如果发生这种情况,那么原始命令行将永远丢失;唯一已知的副本被覆盖。
换句话说,任何这样的内核设施都是
难以实施 可能存在安全问题但最可能的原因仅仅是此类设施的用例有限。
【讨论】:
以上是关于有没有办法改变 Unix 中另一个进程的环境变量?的主要内容,如果未能解决你的问题,请参考以下文章
有没有办法将类型声明中特定键的值用作同一声明中另一个键的类型(在 Typescript 中)?
Linux中另一个进程的重复文件描述符(没有sendmsg)