PowerShell 将命名参数传递给 ArgumentList
PowerShell Pass Named parameters to ArgumentList
我有一个接受 3 个命名参数的 PowerShell 脚本。请让我知道如何从命令行传递相同的信息。我尝试了下面的代码,但同样不起作用。它仅将整个值分配给 P3。我的要求是P1应该包含1,P2应该包含2,P3应该分配3.
Invoke-Command -ComputerName server -FilePath "D:\test.ps1" -ArgumentList {-P1 1 -P2 2 -P3 3}
下面是脚本文件代码。
Param (
[string]$P3,
[string]$P2,
[string]$P1
)
Write-Output "P1 Value :" $P1
Write-Output "P2 Value:" $P2
Write-Output "P3 Value :" $P3
使用哈希表:
icm -ComputerName test -ScriptBlock{$args} -ArgumentList @{"p1"=1;"p2"=2;"p3"=3}
一个选项:
$params = @{
P1 = 1
P2 = 2
P3 = 3
}
$ScriptPath = 'D:\Test.ps1'
$sb = [scriptblock]::create(".{$(get-content $ScriptPath -Raw)} $(&{$args} @params)")
Invoke-Command -ComputerName server -ScriptBlock $sb
mjolinor 的代码效果很好,但我花了几分钟才理解它。
代码使一件简单的事情 - 生成带有内置参数的脚本块的内容:
&{
Param (
[string]$P3,
[string]$P2,
[string]$P1
)
Write-Output "P1 Value:" $P1
Write-Output "P2 Value:" $P2
Write-Output "P3 Value:" $P3
} -P1 1 -P2 2 -P3 3
然后这个脚本块被传递给Invoke-Command。
为了简化代码:
".{$(get-content $ScriptPath -Raw)} $(&{$args} @params)"
$scriptContent = Get-Content $ScriptPath -Raw
$formattedParams = &{ $args } @params
# The `.{}` statement could be replaced with `&{}` here, because we don't need to persist variables after script call.
$scriptBlockContent = ".{ $scriptContent } $formattedParams"
$sb = [scriptblock]::create($scriptBlockContent)
让我们做一个基本的 C# 实现:
void Run()
{
var parameters = new Dictionary<string, string>
{
["P1"] = "1",
["P2"] = "2",
["P3"] = "3"
};
var scriptResult = InvokeScript("Test.ps1", "server", parameters)
Console.WriteLine(scriptResult);
}
string InvokeScript(string filePath, string computerName, Dictionary<string, string> parameters)
{
var innerScriptContent = File.ReadAllText(filePath);
var formattedParams = string.Join(" ", parameters.Select(p => $"-{p.Key} {p.Value}"));
var scriptContent = "$sb = { &{ " + innerScriptContent + " } " + formattedParams + " }\n" +
$"Invoke-Command -ComputerName {computerName} -ScriptBlock $sb";
var tempFile = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".ps1");
File.WriteAllText(tempFile, scriptContent);
var psi = new ProcessStartInfo
{
FileName = "powershell",
Arguments = $@"-ExecutionPolicy Bypass -File ""{tempFile}""",
RedirectStandardOutput = true,
UseShellExecute = false
};
var process = Process.Start(psi);
var responseText = process.StandardOutput.ReadToEnd();
File.Delete(tempFile);
return responseText;
}
代码生成一个临时脚本并执行它。
示例脚本:
$sb = {
&{
Param (
[string]$P3,
[string]$P2,
[string]$P1
)
Write-Output "P1 Value:" $P1
Write-Output "P2 Value:" $P2
Write-Output "P3 Value:" $P3
} -P1 1 -P2 2 -P3 3
}
Invoke-Command -ComputerName server -ScriptBlock $sb
这是一个简单的解决方案:
[PowerShell]::Create().AddCommand('D:\test.ps1').AddParameters(@{ P1 = 1; P2 = 2; P3 = 3 }).Invoke()
这是输出:
PS C:\Windows\system32> [PowerShell]::Create().AddCommand('D:\test.ps1').AddParameters(@{ P1 = 1; P2 = 2; P3 = 3 }).Invoke()
P1 Value :
1
P2 Value:
2
P3 Value :
3
确实使用哈希表!
#TestPs1.ps1
Param (
[string]$P3,
[string]$P2,
[string]$P1
)
Write-Output "P1 Value :" $P1
Write-Output "P2 Value:" $P2
Write-Output "P3 Value :" $P3
$params = @{
P3 = 3
P2 = 2
}
#(just to prove it doesn't matter which order you put them in)
$params["P1"] = 1;
#Trhough the use of the "Splat" operator, we can add parameters directly onto the module
& ".\TestPs1.ps1" @params
输出:
P1 Value :
1
P2 Value:
2
P3 Value :
3
如果您尝试将 -FilePath 与命名参数 (-P1 1 -P2 2) 一起使用,那么我发现这会起作用。使用脚本块 运行 文件,而不是使用 -FilePath.
Invoke-Command -ComputerName server -ScriptBlock {& "D:\test.ps1" -P1 1 -P2 2 -P3 3}
如果您愿意完全跳过 Invoke-Command...
您的脚本可能如下所示:
([string]$args).split('-') | %{
if ($_.Split(' ')[0].ToUpper() -eq "P1") { $P1 = $_.Split(' ')[1] }
elseif ($_.Split(' ')[0].ToUpper() -eq "P2") { $P2 = $_.Split(' ')[1] }
elseif ($_.Split(' ')[0].ToUpper() -eq "P3") { $P3 = $_.Split(' ')[1] }
}
Write-Output "P1 Value :" $P1
Write-Output "P2 Value :" $P2
Write-Output "P3 Value :" $P3
你会这样称呼它:
D:\test.ps1 -P1 1 -P2 2 -P3 3
我有一个接受 3 个命名参数的 PowerShell 脚本。请让我知道如何从命令行传递相同的信息。我尝试了下面的代码,但同样不起作用。它仅将整个值分配给 P3。我的要求是P1应该包含1,P2应该包含2,P3应该分配3.
Invoke-Command -ComputerName server -FilePath "D:\test.ps1" -ArgumentList {-P1 1 -P2 2 -P3 3}
下面是脚本文件代码。
Param (
[string]$P3,
[string]$P2,
[string]$P1
)
Write-Output "P1 Value :" $P1
Write-Output "P2 Value:" $P2
Write-Output "P3 Value :" $P3
使用哈希表:
icm -ComputerName test -ScriptBlock{$args} -ArgumentList @{"p1"=1;"p2"=2;"p3"=3}
一个选项:
$params = @{
P1 = 1
P2 = 2
P3 = 3
}
$ScriptPath = 'D:\Test.ps1'
$sb = [scriptblock]::create(".{$(get-content $ScriptPath -Raw)} $(&{$args} @params)")
Invoke-Command -ComputerName server -ScriptBlock $sb
mjolinor 的代码效果很好,但我花了几分钟才理解它。
代码使一件简单的事情 - 生成带有内置参数的脚本块的内容:
&{
Param (
[string]$P3,
[string]$P2,
[string]$P1
)
Write-Output "P1 Value:" $P1
Write-Output "P2 Value:" $P2
Write-Output "P3 Value:" $P3
} -P1 1 -P2 2 -P3 3
然后这个脚本块被传递给Invoke-Command。
为了简化代码:
".{$(get-content $ScriptPath -Raw)} $(&{$args} @params)"
$scriptContent = Get-Content $ScriptPath -Raw
$formattedParams = &{ $args } @params
# The `.{}` statement could be replaced with `&{}` here, because we don't need to persist variables after script call.
$scriptBlockContent = ".{ $scriptContent } $formattedParams"
$sb = [scriptblock]::create($scriptBlockContent)
让我们做一个基本的 C# 实现:
void Run()
{
var parameters = new Dictionary<string, string>
{
["P1"] = "1",
["P2"] = "2",
["P3"] = "3"
};
var scriptResult = InvokeScript("Test.ps1", "server", parameters)
Console.WriteLine(scriptResult);
}
string InvokeScript(string filePath, string computerName, Dictionary<string, string> parameters)
{
var innerScriptContent = File.ReadAllText(filePath);
var formattedParams = string.Join(" ", parameters.Select(p => $"-{p.Key} {p.Value}"));
var scriptContent = "$sb = { &{ " + innerScriptContent + " } " + formattedParams + " }\n" +
$"Invoke-Command -ComputerName {computerName} -ScriptBlock $sb";
var tempFile = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".ps1");
File.WriteAllText(tempFile, scriptContent);
var psi = new ProcessStartInfo
{
FileName = "powershell",
Arguments = $@"-ExecutionPolicy Bypass -File ""{tempFile}""",
RedirectStandardOutput = true,
UseShellExecute = false
};
var process = Process.Start(psi);
var responseText = process.StandardOutput.ReadToEnd();
File.Delete(tempFile);
return responseText;
}
代码生成一个临时脚本并执行它。
示例脚本:
$sb = {
&{
Param (
[string]$P3,
[string]$P2,
[string]$P1
)
Write-Output "P1 Value:" $P1
Write-Output "P2 Value:" $P2
Write-Output "P3 Value:" $P3
} -P1 1 -P2 2 -P3 3
}
Invoke-Command -ComputerName server -ScriptBlock $sb
这是一个简单的解决方案:
[PowerShell]::Create().AddCommand('D:\test.ps1').AddParameters(@{ P1 = 1; P2 = 2; P3 = 3 }).Invoke()
这是输出:
PS C:\Windows\system32> [PowerShell]::Create().AddCommand('D:\test.ps1').AddParameters(@{ P1 = 1; P2 = 2; P3 = 3 }).Invoke()
P1 Value :
1
P2 Value:
2
P3 Value :
3
确实使用哈希表!
#TestPs1.ps1
Param (
[string]$P3,
[string]$P2,
[string]$P1
)
Write-Output "P1 Value :" $P1
Write-Output "P2 Value:" $P2
Write-Output "P3 Value :" $P3
$params = @{
P3 = 3
P2 = 2
}
#(just to prove it doesn't matter which order you put them in)
$params["P1"] = 1;
#Trhough the use of the "Splat" operator, we can add parameters directly onto the module
& ".\TestPs1.ps1" @params
输出:
P1 Value :
1
P2 Value:
2
P3 Value :
3
如果您尝试将 -FilePath 与命名参数 (-P1 1 -P2 2) 一起使用,那么我发现这会起作用。使用脚本块 运行 文件,而不是使用 -FilePath.
Invoke-Command -ComputerName server -ScriptBlock {& "D:\test.ps1" -P1 1 -P2 2 -P3 3}
如果您愿意完全跳过 Invoke-Command...
您的脚本可能如下所示:
([string]$args).split('-') | %{
if ($_.Split(' ')[0].ToUpper() -eq "P1") { $P1 = $_.Split(' ')[1] }
elseif ($_.Split(' ')[0].ToUpper() -eq "P2") { $P2 = $_.Split(' ')[1] }
elseif ($_.Split(' ')[0].ToUpper() -eq "P3") { $P3 = $_.Split(' ')[1] }
}
Write-Output "P1 Value :" $P1
Write-Output "P2 Value :" $P2
Write-Output "P3 Value :" $P3
你会这样称呼它:
D:\test.ps1 -P1 1 -P2 2 -P3 3