英文:
Use environment variable set by a batch script in the next batch script run
问题
实际上,我想运行两个批处理脚本,第一个脚本将设置一个系统变量 set NEWPATH="E:/Some"
,第二个脚本将显示该变量的路径:echo %NEWPATH%
。在同一台服务器上,第一次运行时这个方法不起作用,只有在重新启动服务器后才会显示路径,否则将不显示任何内容。所以有人可以帮我解决这个问题吗?
英文:
Actually i want to run 2 bat scripts , first script will set a system variable set NEWPATH="E:/Some"
and second script will show the path of that variable: echo %NEWPATH%
. this is not working with the same server at first time , when i restart server that will show the path otherwise it'll show nothing. so can anyone help me on that?
答案1
得分: 1
我不完全理解你的问题,但是我有几点观察。
一些理论
-
在批处理文件中设置的环境变量(由shell进程
cmd.exe
执行)或者任何其他类型的进程,只能为该进程设置。换句话说,每个进程在创建时由操作系统提供了一个特殊的环境变量块。
这也意味着当设置环境变量的进程结束时,它的环境块也会消失。
-
设置了环境变量的进程可以被由设置该环境变量的进程启动的进程继承。
在你的情况下会发生什么
这意味着如果你按顺序运行两个批处理脚本,情况如下:
-
第一个
cmd.exe
进程启动,执行批处理脚本设置一个环境变量;然后该进程结束,它的环境块也消失了。 -
然后启动另一个
cmd.exe
,它继承了你的主机进程(使用Go编写)的环境块,但是你可以看到,第一个批处理脚本设置的任何内容都不可用。
你可以怎么解决这个问题
有两种可能的解决方法:
-
让第一个批处理脚本自己调用第二个脚本。
在这种情况下,第二个
cmd.exe
将继承第一个脚本设置的环境变量,因此可以“看到”它们。注意,Windows批处理命令语言支持使用
calls
命令通过路径名调用其他批处理脚本。 -
让第一个脚本将该变量的值传递给主机进程,然后在主机进程中安排第二个
cmd.exe
在其环境中具有指定的变量和指定的值。例如,第一个脚本可以执行以下操作:
echo VARNAME=value
这样就会将
VARNAME=value
打印到标准输出。
你的Go进程可以解析该输出,在
=
字符上进行分解,对不影响“有趣”变量(如PATH
、USERPROFILE
等)进行清理,然后主机进程可以执行以下操作:env := os.Environ() env = env.append("VARNAME=value") // 这里是实际值 ... cmd := exec.Command("cmd.exe", "/c", secondScriptFilePath) cmd.Env = env ... cmd.Run() // 或者其他操作
第二种情况可以稍微不同:主机进程可以调用os.Setenv("VARNAME=value")
将该变量添加到自己的环境块中;然后,它将自动被它启动的任何进程继承。
更新以回应楼主的评论
> …脚本文件将在客户端上,所以我无法添加一行echo VARNAME=value
。
> 那么还有其他可能的方法吗?
在你的情况下,还有另一种方法可能有效。
思路是cmd.exe
只是一个shell:当它以非交互方式启动时(exec.Command("cmd.exe")
就是这样做的),它从标准输入流中读取命令,并逐个执行它们,直到流被关闭(由发送者关闭)。
因此,你可以按照以下步骤进行操作:
-
启动
cmd.exe
,同时将io.Pipe
的一个实例连接到其Stdin
。 -
自己打开第一个脚本,并通过在第一步设置的管道将其传输给正在运行的
cmd.exe
。不要忘记
Flush()
管道。你可以使用
io.Copy()
函数将所有数据从打开的io.Reader
发送到某个io.Writer
。 -
保持shell实例运行。
-
在适当的时候,读取第二个脚本,并将其传输给同一个shell。
由于是同一个shell,第二个脚本将运行,就好像它是物理上附加到第一个脚本上的,并且将看到第一个脚本设置的所有变量。
英文:
I don't fully grasp your problem, but here's a couple of observations.
Some theory
-
Environment vaiables set in a batch file (which is being executed by a shell process,
cmd.exe
)—or any other kind of process—can only be set for that very process.That is, each process has a special block with environment variables made available to it by the OS when that process is created.
This also means that when the process which set an environment variable is finished, it's environment block is gone.
-
An environment variable can be inherited by a process which was started by the process which has set that environment variable.
What happens in your case
What this means is that if you run two batch scripts in a sequence, this means
-
The first
cmd.exe
process is strated, the batch script it executed
set an environment variable; then the process is gone, and its environment block is gone, too. -
Then another
cmd.exe
is started, it inherits the environment block of your host process (written Go), but as you can see, whatever is set by a first batch script is not available.
What you can do about it
There are two possible ways to solve the problem:
-
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 likePATH
,
USERPROFILE
etc, and then the host process could doenv := os.Environ() env = env.append("VARNAME=value") // real value here ... cmd := exec.Command(`cmd.exe`, `/c`, secondScriptFilePath) cmd.Env = env ... cmd.Run() // or whatever
The second case can be done a bit differently: the host process
can call os.Setenv("VARNAME=value")
to make this variable appear in its
own environment block; it then will be inherited automatically by whatever processes it starts after that.
Update to address the OP's comment
> …script files will be in client side so i can't add a line
> echo VARNAME=value
.
> so is there any other possible way to do this?
There's another approach which might work in your case.
The idea is that cmd.exe
is just a shell: when it's started non-interactively (that's what is done by exec.Command("cmd.exe")
)
it reads commands from its standard input stream and executes them one by one—until the stream is closed (by the sender).
Hence you can do the following:
-
Start
cmd.exe
while connecting an instance ofio.Pipe
to itsStdin
. -
Open the first script yourself and shovel it at the running
cmd.exe
via the pipe set up on the first step.Don't forget to
Flush()
the pipe.You can use the
io.Copy()
function which sends all the data from an openedio.Reader
to someio.Writer
. -
Leave the shell instance running.
-
When it's time, read the second script and shovel it at the same shell.
Since the shell is the same, the second script would run as if it were physically appended to the first one, and will see all the variables set by the first script.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论