【问题标题】:Change parent shell's environment from a subprocess从子进程更改父 shell 的环境
【发布时间】:2014-10-22 15:13:38
【问题描述】:

如果我有一个用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 中的环境变量。我正在寻找一个可行的解决方案,不一定是优雅的。

【问题讨论】:

  • 以下是常规解决问题的方法:设置PS1=[\w] 将适用于目录并将export http_proxy=http://foo.example.com:8080/ 添加到/etc/profile.d/foo.sh
  • 我已经编辑了你的标题和标签:不同语言的子进程严格来说不是子shell——子shell是你从没有执行程序的fork中得到的,而进入Python解释器不仅需要一个 fork,还需要一个 exec 调用。
  • @BrianCain,设置PS1 不是我想做的。我想cd 进入那个目录。此外,profile.d 解决方案:1) 需要 root 访问权限才能即时执行,2) 仅在新的应启动时运行。
  • @CharlesDuffy,很公平。但是,我不想指定 python。将重新编辑。
  • 哦,我明白了。 CDPATH 非常接近此功能。不过,您仍然需要输入 cd project5

标签: python linux bash shell


【解决方案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))

...因为不小心使用会导致 shell 注入攻击。


相比之下,如果您在 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"

如果从您的问题中可以看出,您希望您的命令 appear 被编写为原生 shell 函数,我建议将其包装成一个(这种做法也可以与 @ 987654330@)。例如,安装可能涉及在一个人的.bashrc 中添加以下内容:

my_command() {
  eval "$(command /path/to/my_command.py "$@")"
}

...这样用户就不需要输入eval

【讨论】:

    【解决方案2】:

    基本上,查尔斯·达菲(Charles Duffy)一针见血,我在这里提出另一个关于这个问题的观点。

    您基本上要问的是进程间通信:您有一个进程,它可能是也可能不是 shell 的子进程(我认为这并不重要),并且您希望该进程进行信息通信到原来的shell(只是另一个进程,顺便说一句),并让它改变它的状态。

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

    trap 'cd /tmp; pwd;' SIGUSR2
    

    现在:

    1. 在你的 shell 中输入 echo $$,这会给你一个数字,PID
    2. cd 到 shell 中的一个目录(/tmp 以外的任何目录)
    3. 转到另一个 shell(在另一个窗口或你有什么),然后键入:kill SIGUSR2 PID
    4. 您会发现您在原来的 shell 中的 /tmp 中。

    这就是沟通渠道的一个例子。魔鬼当然在细节中。您的问题有两个方面:如何让 shell 与您的程序通信(如果这对您有用,command_not_found_handle 会很好地做到这一点),以及如何让您的程序与 shell 通信。下面,我将讨论后一个问题:

    例如,您可以在原始 shell 中使用陷阱语句:

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

    ...你喜欢的命令将被赋予原始 shell 的当前工作目录作为第一个参数,以及 shell 的进程 id(因此它知道向谁发出信号),它可以对其进行操作。如果你的命令给eval命令发送一个可执行的shell命令字符串,它将在原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
    

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

    希望 command_not_found_handle 在当前 shell 环境中做某事的人可以使用信号来获得他想要的效果。在这里,我手动运行 kill 但没有理由为什么 shell 函数无法执行此操作。

    在前端做一些花哨的工作,即您重新解释或预先解释用户对 shell 的输入,可能需要用户运行一个可能非常复杂的前端程序,这取决于您想要做什么。老派的“期待”计划非常适合这样的事情,但现在没有太多年轻人选择 TCL :-)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-14
      相关资源
      最近更新 更多