从子进程更改父shell的环境

时间:2014-10-22 15:13:38

标签: python linux bash shell

如果我有一个用bash以外的语言编写的程序(比如python),我怎样才能更改环境变量或其中的当前工作目录,使其反映在调用shell中?

我想用它来编写一个简化常见操作的'命令行帮助器'。例如,聪明的cd。当我在提示符中输入目录名称时,它应该cd进入它。

[~/]$ Downloads
[~/Downloads]$

甚至

[~/]$ project5
[~/projects/project5]$

然后我找到How to change current working directory inside command_not_found_handle(这正是我想要做的事情之一),它将我介绍给shopt -s autocd。但是,这仍然无法处理提供的目录不在./

的情况

另外,如果我想做一些事情,比如从python脚本设置http_proxy变量,甚至更新PATH变量,我的选择是什么?

P上。 S.我知道可能没有一种明显的方法可以在python脚本中编写一个魔法命令来自动更新调用shell中的环境变量。我正在寻找一个有效的解决方案,不一定是优雅的。

2 个答案:

答案 0 :(得分:1)

这只能通过父shell的参与和帮助来完成。对于执行此操作的程序的实际示例,您可以查看应该如何使用ssh-agent

eval "$(ssh-agent -s)"

...从ssh-agent读取输出并在当前shell中运行它(-s指定与Bourne兼容的输出,与csh相比)。


如果您使用的是Python,请务必使用pipes.quote()(或者,对于Python 3.x,shlex.quote())安全地处理输出:

import pipes
dirname='/path/to/directory with spaces'
foo_val='value with * wildcards * that need escaping and \t\t tabs!'
print 'cd %s; export FOO=%s;' % (pipes.quote(dirname), pipes.quote(foo_val))

......因为粗心使用否则会导致炮弹攻击。


相比之下,如果您在bash中将其写为外部脚本,请务必使用printf %q进行安全转义(但请注意,其输出的目标是其他bash shell,而不是POSIX sh合规性) :

#!/bin/bash
dirname='/path/to/directory with spaces'
foo_val='value with * wildcards * that need escaping and \t\t tabs!'
printf 'cd %q; export FOO=%q;' "$dirname" "$foo_val"

如果从您的问题中看起来,您希望出现的命令被写为本机shell函数,我建议将其包装在一个中(这种做法也可以用于{ {1}})。例如,安装可能涉及将以下内容放入command_not_found_handle

.bashrc

...这样,用户无需输入my_command() { eval "$(command /path/to/my_command.py "$@")" }

答案 1 :(得分:1)

基本上,查尔斯达菲击中了头部的钉子,我在这里提出了另一个问题。

你基本上要问的是进程间通信:你有一个进程,它可能是也可能不是shell的子进程(我不认为这太重要了),你想要那个将信息传递给原始shell(只是另一个进程,顺便说一句),让它改变状态。

一种可能性是使用信号。例如,在shell中,您可以:

trap 'cd /tmp; pwd;' SIGUSR2

现在:

  1. 在shell中键入echo $$,这将为您提供一个数字 PID
  2. cd到shell中的目录(/ tmp以外的任何目录)
  3. 转到另一个shell(在另一个窗口或你有什么),然后输入:kill SIGUSR2 PID
  4. 您会发现原来的shell中有/ tmp。
  5. 这就是通信渠道的一个例子。魔鬼当然是细节。你的问题有两半:如何让shell与你的程序进行通信(如果这对你有用,command_not_found_handle会很好地完成),以及如何让你的程序与shell通信。下面,我将讨论后一个问题:

    例如,你可以在原始shell中有一个trap语句:

    trap 'eval $(/path/to/my/fancy/command $(pwd) $$)' SIGUSR2
    

    ...您的花哨命令将被赋予原始shell的当前工作目录作为第一个参数,以及shell的进程ID(因此它知道发出信号的人),并且它可以对其进行操作。如果您的命令将可执行shell命令字符串发送到eval命令,它将在原始shell的环境中执行。

    例如:

    trap 'eval $(/tmp/doit $$ $(pwd)); pwd;' SIGUSR2
    

    / tmp / doit是花哨的命令。它可以是任何可执行类型[Python,C,Perl等],关键是它吐出一个shell可以评估的字符串。在/ tmp / doit中,我提供了一个bash脚本:

    #!/bin/bash
    echo "echo PID: $1 original directory: $2; cd /tmp"
    

    (我确保该文件可执行:chmod 755 / tmp / doit)。现在,如果我输入:

    cd; echo $$
    

    然后,在另一个shell中,通过上面的echo获取数字输出(" NNNNN")并执行:

    kill -s SIGUSR2 NNNNN
    

    ...突然之间,我会在原始shell中看到这样的内容:

    PID: NNNNN original directory: /home/myhomepath
    /tmp
    

    如果我输入" pwd"在我原来的shell中,我会看到我在/ tmp。

    希望command_not_found_handle在当前shell环境中执行某些操作的人可能已经使用信号来获得他想要的效果。在这里我手动运行了kill,但没有理由为shell函数做不到。

    在前端做一些奇特的工作,你重新解释或预先解释用户对shell的输入,可能要求用户运行一个非常复杂的前端程序,这取决于你想要什么去做。旧学校"期待"程序是这样的事情的理想选择,但这些天没有太多的年轻人选择TCL :-)。