我正在用 PowerShell 替换 .bat 脚本的部分内容。批处理文件的配置是通过
set
适当的环境变量的文件完成的。我正在寻找一种将这些变量值加载到 .ps1
脚本中的方法,而不修改 .bat
文件(因为它们也在其他地方使用。
示例
.bat
如下所示:
set VAR_ONE=some_value
set VAR_TWO=/other-value
在批处理脚本中,我只需
CALL
配置文件和变量就可用。我已经尝试过点采购 (. filename.bat
) 和从 PowerShell 调用 (& filename.bat
) 配置文件,这两种方法都没有使变量可见。尝试使用 $VAR_ONE
和 $env:VAR_ONE
语法访问它们。
在不修改磁盘格式的情况下加载此类配置文件的好方法是什么?
如果您使用的是 PowerShell 社区扩展,它有一个
Invoke-BatchFile
可以执行此操作。我使用 Visual Studio vcvarsall.bat 文件来配置我的 PowerShell 会话以使用 Visual Studio 工具。
首选选项是将配置更改为
.ps1
文件并将变量定义更改为 PowerShell 语法:$VAR_ONE = 'some_value'
$VAR_TWO = '/other-value'
然后您将能够对文件进行点源:
. filename.ps1
如果您想坚持使用当前的格式,则必须解析这些值,例如像这样:
Select-String '^set ([^=]*)=(.*)' .\filename.bat | ForEach-Object {
Set-Variable $_.Matches.Groups[1].Value $_.Matches.Groups[2].Value
}
注意:
以上内容在 v3 之前的 PowerShell 版本中不起作用。 v2 兼容版本将如下所示:Select-String '^set ([^=]*)=(.*)' .\filename.bat | ForEach-Object {
$_.Matches
} | ForEach-Object {
Set-Variable $_.Groups[1].Value $_.Groups[2].Value
}
您可以通过批处理文件来完成此操作,首先
call
配置文件,然后然后执行PowerShell脚本。
假设.bat名为
test.bat
,定义
testps.ps1
:$lines = cat "test.bat"
$output = @{};
foreach ($line in $lines) {
$bits = $line.Split("=");
$name = $bits[0].Split(" ")[1];
$val = $bits[1];
$output[$name] = $val
}
return $output
那么结果是这样的:
C:\temp> .\testps.ps1
Name Value
---- -----
VAR_TWO /other-value
VAR_ONE some_value
C:\temp> $x = .\testps.ps1
C:\temp> $x
Name Value
---- -----
VAR_TWO /other-value
VAR_ONE some_value
C:\temp> $x["VAR_ONE"]
some_value
可能有更好的分割方法(如果我找到它,将进行编辑)
从 PowerShell 调用批处理文件必然会在子进程
中执行批处理文件,因为必须启动
cmd.exe
子进程才能执行它。在子进程(批处理文件)中设置的任何环境变量都不会被调用进程(PowerShell)看到
。因此,需要一个解决方法
:cmd /c 'sample.cmd >NUL & SET' |
Where-Object { $_ -notmatch '^PROMPT=' } |
ForEach-Object {
# Split the "<name>=<value>" line into the variable's name and value.
$name, $value = $_ -split '=', 2
# Define it as a process-level environment variable in PowerShell.
Set-Content ENV:$name $value
}
注意
:基本假设是目标批处理文件不
使用
setlocal
(因为这会将变量的范围限制为该批处理文件);也就是说,批处理文件的明确目的是按设计为调用者设置环境变量,但它却不会。
假设调用批处理文件的唯一目的是捕获批处理文件设置的环境变量;因此,后者的 output - 如果有的话 - 被丢弃 (
>NUL
),这简化了输出的解析。
执行
SET
(在批处理文件调用之后无条件地通过 &
)将所有当时的环境变量定义打印为 <name>=<value>
对。
cmd.exe
还定义了一个PROMPT
环境变量,该变量与PowerShell或其他进程无关,所以上面将其过滤掉。ForEach-Object
调用,$name, $value = $_ -split '=', 2
然后将每个 <name>=<value>
对分成其组成部分。
上面的假设批处理文件环境变量也应该在PowerShell中定义为环境变量,使用
Env:
PowerShell驱动器和Set-Content
。
Set-Variable
,则将创建仅限 PowerShell 变量,这些变量只能由当前 PowerShell 会话看到,而不能由从中调用的 子进程看到。上述解决方案没有考虑批处理文件可能执行的环境变量的删除;还需要做更多的工作来传播清除情况。