使用由批处理脚本设置的环境变量在下一次批处理脚本运行中。

3

实际上我想运行2个批处理脚本,第一个脚本将设置一个系统变量set NEWPATH="E:/Some",第二个脚本将显示该变量的路径:echo %NEWPATH%。在同一台服务器上第一次无法正常工作,只有在重新启动服务器后才能显示路径,否则什么也不会显示。请问是否有人能帮我解决这个问题?


请参考 setx 命令,该命令可以在用户或系统上下文中永久设置环境变量(这是第三种可能的方式,@kostix)... - aschipfl
@aschipfl,谢谢!我不知道这个命令。问题是我不知道任何简单的解决方案来用更新后的环境块替换当前进程的环境块。嗯,除了直接调用Win32 API之外,没有其他办法。另一个明显的问题是,假设在执行序列中的两个进程之间以这种方式通信一个单一的键值对组合,这是过度工程化的。 - kostix
@shagul,顺便问一下,你在第一个批处理脚本中是否已经使用了setx命令?(这可以解释为什么“当我重新启动服务器时,它会显示路径,否则它将什么也不显示”这句话对我来说有些难以理解)。 - kostix
func one(){ cmd := exec.Command("cmd", "/C", "bat1.bat") cmd.Start() timer := time.AfterFunc(1*time.Second, func() { err := cmd.Process.Kill() if err != nil { panic(err) } }) err := cmd.Wait() timer.Stop()// 从这里读取错误,你会注意到kill fmt.Println(err)}..我在func two()中做了同样的事情 - shagul
批处理文件只包含一行代码 "setx /M My_PATH "C:/path""。 - shagul
显示剩余3条评论
1个回答

1
我不完全了解你的问题,但这里有几个观察结果。
一些理论:
- 在批处理文件中设置的环境变量(由shell进程cmd.exe执行)或其他任何类型的进程,只能为该进程设置。 - 每个进程在创建时都由操作系统提供了一个特殊的块,其中包含可用于该进程的环境变量。 - 这也意味着,当设置环境变量的进程完成后,它的环境块就会消失。 - 一个环境变量可以被设置该环境变量的进程启动的进程继承。
你的情况下会发生什么:
这意味着如果你按顺序运行两个批处理脚本,这意味着:
  1. 首先启动了一个cmd.exe进程,执行了一个批处理脚本,设置了一个环境变量;然后该进程消失,它的环境块也随之消失。

  2. 接着又启动了另一个cmd.exe进程,继承了您主机进程(编写Go)的环境块,但是可以看到,第一个批处理脚本设置的任何内容都不可用。

你可以怎么做

有两种可能的解决方法:

  • Make the first batch script call the second one by itself.

    In this case the second cmd.exe will inherit the environment variables set by the first script and will hence "see" them.

    Note that the Windows batch command language supports the calls command to call out to other batch scripts by their pathnames.

  • Make the first script communicate the value of that variable to your host process and then arrange in the host process for the second cmd.exe to have the indicated variable with the indicated value in its environment.

    Say, the first script could just do something like

     echo VARNAME=value
    

    to have

    VARNAME=value
    

    printed to its stdout.

    Your Go process could parse that output, tear it apart on the = character, sanitize to not affect "interesting" variables like PATH, USERPROFILE etc, and then the host process could do

    env := os.Environ()
    env = env.append("VARNAME=value") // real value here
    ...
    cmd := exec.Command(`cmd.exe`, `/c`, secondScriptFilePath)
    cmd.Env = env
    ...
    cmd.Run() // or whatever
    
第二种情况可以稍微不同:主机进程可以调用os.Setenv("VARNAME=value")将此变量显示在其自己的环境块中;然后,它将自动继承启动后的任何进程。

更新以解决OP的评论

…脚本文件将在客户端中,因此我无法添加一行echo VARNAME=value。那么还有其他可能的方法吗?

在您的情况下,还有另一种方法可能有效。

想法是cmd.exe只是一个Shell:当它以非交互方式启动时(这就是exec.Command("cmd.exe")所做的),它从其标准输入流读取命令并逐个执行它们-直到流被发送方关闭为止。

因此,您可以执行以下操作:

  1. 使用 io.Pipe 将一个实例连接到 cmd.exeStdin 上来启动 cmd.exe

  2. 打开第一个脚本并通过在第一步设置的管道将其传输到正在运行的 cmd.exe 中。

    不要忘记对该管道进行 Flush() 操作。

    您可以使用 io.Copy() 函数,该函数将从已打开的 io.Reader 发送所有数据到某个 io.Writer

  3. 保留 shell 实例运行状态。

  4. 在适当的时候读取第二个脚本并将其传输到同一个 shell 中。

由于使用的是同一个 shell,因此第二个脚本将会像物理附加到第一个脚本一样运行,并且可以访问第一个脚本设置的所有变量。


非常感谢您的帮助,但脚本文件将在客户端执行,因此我无法添加一行 [echo VARNAME=value]。那么还有其他可能的方法吗? - shagul
@shagul,可能是的 - 请参考我的回答更新。 - kostix

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接