PowerShell 启动进程参数列表转义 '|'变量中的管道字符
PowerShell Start-Process argumentlist escape '|' pipe character in a variable
当运行 Start-Process
与-ArgumentList
并传递字符串数组$configArgs
时,它有一个包含特殊字符的字符串,它是一个竖线(|
).管道字符来自最后一个变量 $passwordtemp
添加了 --windowsLogonPassword
.
由于管道字符,我收到以下错误消息,
“文件名、目录名或卷标语法不正确。”
有什么办法可以避免这种情况吗?
[CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = "NoVersion")]
param(
[parameter(Mandatory = $false)]
[string]$AgentDirectory = [IO.Path]::Combine($env:USERPROFILE, "VSTSAgents"),
[parameter(Mandatory = $false)]
[string]$Work,
[parameter(Mandatory = $false)]
[string]$Name = [System.Environment]::MachineName + "-$(Get-Random)",
[parameter(Mandatory = $false)]
[string]$Pool = 'Default',
[parameter(Mandatory = $true)]
[string]$PAT,
[parameter(Mandatory = $true)]
[uri]$ServerUrl,
[parameter(Mandatory = $false)]
[switch]$Replace,
[parameter(Mandatory = $false)]
[pscredential]$LogonCredential,
[parameter(Mandatory = $false)]
[string]$Cache = [io.Path]::Combine($env:USERPROFILE, ".vstsagents")
)
if ($PSVersionTable.Platform -and $PSVersionTable.Platform -ne 'Win32NT') {
throw "Not Implemented: Support for $($PSVersionTable.Platform), contributions welcome."
}
if ( $Verbose ) { $VerbosePreference = 'Continue' }
$existing = Get-VSTSAgent -AgentDirectory $AgentDirectory -NameFilter $Name
if ( $existing ) {
if ($Replace) {
Uninstall-VSTSAgent -NameFilter $Name -AgentDirectory $AgentDirectory -PAT $PAT -ErrorAction Stop
}
else { throw "Agent $Name already exists in $AgentDirectory" }
}
$findArgs = @{ 'Platform' = 'win' }
if ( $MinimumVersion ) { $findArgs['MinimumVersion'] = $MinimumVersion }
if ( $MaximumVersion ) { $findArgs['MaximumVersion'] = $MaximumVersion }
if ( $RequiredVersion ) { $findArgs['RequiredVersion'] = $RequiredVersion }
$agent = Find-VSTSAgent @findArgs | Sort-Object -Descending -Property Version | Select-Object -First 1
if ( -not $agent ) { throw "Could not find agent matching requirements." }
Write-Verbose "Installing agent at $($agent.Uri)"
$fileName = $agent.Uri.Segments[$agent.Uri.Segments.Length - 1]
$destPath = [IO.Path]::Combine($Cache, "$($agent.Version)$fileName")
if ( -not (Test-Path $destPath) ) {
$destDirectory = [io.path]::GetDirectoryName($destPath)
if (!(Test-Path $destDirectory -PathType Container)) {
New-Item "$destDirectory" -ItemType Directory | Out-Null
}
Write-Verbose "Downloading agent from $($agent.Uri)"
try { Start-BitsTransfer -Source $agent.Uri -Destination $destPath }
catch { throw "Downloading $($agent.Uri) failed: $_" }
}
else { Write-Verbose "Skipping download as $destPath already exists." }
$agentFolder = [io.path]::Combine($AgentDirectory, $Name)
Write-Verbose "Unzipping $destPath to $agentFolder"
if ( $PSVersionTable.PSVersion.Major -le 5 ) {
Add-Type -AssemblyName System.IO.Compression.FileSystem -ErrorAction Stop
}
[System.IO.Compression.ZipFile]::ExtractToDirectory($destPath, $agentFolder)
$configPath = [io.path]::combine($agentFolder, 'config.cmd')
$configPath = Get-ChildItem $configPath -ErrorAction SilentlyContinue
if ( -not $configPath ) { throw "Agent $agentFolder is missing config.cmd" }
[string[]]$configArgs = @('--unattended', '--url', "$ServerUrl", '--auth', `
'pat', '--pool', "$Pool", '--agent', "$Name", '--runAsService')
if ( $Replace ) { $configArgs += '--replace' }
if ( $LogonCredential ) { $configArgs += '--windowsLogonAccount', $LogonCredential.UserName }
if ( $Work ) { $configArgs += '--work', $Work }
if ( -not $PSCmdlet.ShouldProcess("$configPath $configArgs", "Start-Process") ) { return }
$token = [System.Net.NetworkCredential]::new($null, $PAT).Password
$configArgs += '--token', $token
if ( $LogonCredential ) {
$passwordtemp = [System.Net.NetworkCredential]::new($null, $LogonCredential.Password).Password
$configArgs += '--windowsLogonPassword', $passwordtemp
}
$outFile = [io.path]::Combine($agentFolder, "out.log")
$errorFile = [io.path]::Combine($agentFolder, "error.log")
Write-Verbose "Registering $Name to $Pool at $ServerUrl"
Start-Process $configPath -ArgumentList $configArgs -NoNewWindow -Wait `
-RedirectStandardOutput $outFile -RedirectStandardError $errorFile -ErrorAction Stop
if (Test-Path $errorFile) {
Get-Content $errorFile | Write-Error
}
有两个因素在起作用:
一个long-standing bug in Start-Process
unfortunately requires use of embedded double-quoting around arguments that contain spaces, e.g. -ArgumentList '-foo', '"bar baz"'
- see this answer.
调用批处理文件 (.cmd
) 时,命令行被不恰当地解析为好像它是从 内部 cmd.exe
[ 提交的=38=] by cmd.exe
,要求包含 cmd.exe
个元字符(例如 |
的 space-less 个参数为 double-quoted 或者元字符单独为 ^
-转义。
您可以手动补偿这些行为,如下所示:
$configArgsEscaped =
switch -Regex ($configArgs) {
'[ ^&|<>",;=()]' { '"{0}"' -f ($_ -replace '"', '""') }
default { $_ } # no double-quoting needed
}
现在在 Start-Process
调用中使用 -ArgumentList $configArgsEscaped
代替 -ArgumentList $configArgs
。
当运行 Start-Process
与-ArgumentList
并传递字符串数组$configArgs
时,它有一个包含特殊字符的字符串,它是一个竖线(|
).管道字符来自最后一个变量 $passwordtemp
添加了 --windowsLogonPassword
.
由于管道字符,我收到以下错误消息,
“文件名、目录名或卷标语法不正确。”
有什么办法可以避免这种情况吗?
[CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = "NoVersion")]
param(
[parameter(Mandatory = $false)]
[string]$AgentDirectory = [IO.Path]::Combine($env:USERPROFILE, "VSTSAgents"),
[parameter(Mandatory = $false)]
[string]$Work,
[parameter(Mandatory = $false)]
[string]$Name = [System.Environment]::MachineName + "-$(Get-Random)",
[parameter(Mandatory = $false)]
[string]$Pool = 'Default',
[parameter(Mandatory = $true)]
[string]$PAT,
[parameter(Mandatory = $true)]
[uri]$ServerUrl,
[parameter(Mandatory = $false)]
[switch]$Replace,
[parameter(Mandatory = $false)]
[pscredential]$LogonCredential,
[parameter(Mandatory = $false)]
[string]$Cache = [io.Path]::Combine($env:USERPROFILE, ".vstsagents")
)
if ($PSVersionTable.Platform -and $PSVersionTable.Platform -ne 'Win32NT') {
throw "Not Implemented: Support for $($PSVersionTable.Platform), contributions welcome."
}
if ( $Verbose ) { $VerbosePreference = 'Continue' }
$existing = Get-VSTSAgent -AgentDirectory $AgentDirectory -NameFilter $Name
if ( $existing ) {
if ($Replace) {
Uninstall-VSTSAgent -NameFilter $Name -AgentDirectory $AgentDirectory -PAT $PAT -ErrorAction Stop
}
else { throw "Agent $Name already exists in $AgentDirectory" }
}
$findArgs = @{ 'Platform' = 'win' }
if ( $MinimumVersion ) { $findArgs['MinimumVersion'] = $MinimumVersion }
if ( $MaximumVersion ) { $findArgs['MaximumVersion'] = $MaximumVersion }
if ( $RequiredVersion ) { $findArgs['RequiredVersion'] = $RequiredVersion }
$agent = Find-VSTSAgent @findArgs | Sort-Object -Descending -Property Version | Select-Object -First 1
if ( -not $agent ) { throw "Could not find agent matching requirements." }
Write-Verbose "Installing agent at $($agent.Uri)"
$fileName = $agent.Uri.Segments[$agent.Uri.Segments.Length - 1]
$destPath = [IO.Path]::Combine($Cache, "$($agent.Version)$fileName")
if ( -not (Test-Path $destPath) ) {
$destDirectory = [io.path]::GetDirectoryName($destPath)
if (!(Test-Path $destDirectory -PathType Container)) {
New-Item "$destDirectory" -ItemType Directory | Out-Null
}
Write-Verbose "Downloading agent from $($agent.Uri)"
try { Start-BitsTransfer -Source $agent.Uri -Destination $destPath }
catch { throw "Downloading $($agent.Uri) failed: $_" }
}
else { Write-Verbose "Skipping download as $destPath already exists." }
$agentFolder = [io.path]::Combine($AgentDirectory, $Name)
Write-Verbose "Unzipping $destPath to $agentFolder"
if ( $PSVersionTable.PSVersion.Major -le 5 ) {
Add-Type -AssemblyName System.IO.Compression.FileSystem -ErrorAction Stop
}
[System.IO.Compression.ZipFile]::ExtractToDirectory($destPath, $agentFolder)
$configPath = [io.path]::combine($agentFolder, 'config.cmd')
$configPath = Get-ChildItem $configPath -ErrorAction SilentlyContinue
if ( -not $configPath ) { throw "Agent $agentFolder is missing config.cmd" }
[string[]]$configArgs = @('--unattended', '--url', "$ServerUrl", '--auth', `
'pat', '--pool', "$Pool", '--agent', "$Name", '--runAsService')
if ( $Replace ) { $configArgs += '--replace' }
if ( $LogonCredential ) { $configArgs += '--windowsLogonAccount', $LogonCredential.UserName }
if ( $Work ) { $configArgs += '--work', $Work }
if ( -not $PSCmdlet.ShouldProcess("$configPath $configArgs", "Start-Process") ) { return }
$token = [System.Net.NetworkCredential]::new($null, $PAT).Password
$configArgs += '--token', $token
if ( $LogonCredential ) {
$passwordtemp = [System.Net.NetworkCredential]::new($null, $LogonCredential.Password).Password
$configArgs += '--windowsLogonPassword', $passwordtemp
}
$outFile = [io.path]::Combine($agentFolder, "out.log")
$errorFile = [io.path]::Combine($agentFolder, "error.log")
Write-Verbose "Registering $Name to $Pool at $ServerUrl"
Start-Process $configPath -ArgumentList $configArgs -NoNewWindow -Wait `
-RedirectStandardOutput $outFile -RedirectStandardError $errorFile -ErrorAction Stop
if (Test-Path $errorFile) {
Get-Content $errorFile | Write-Error
}
有两个因素在起作用:
一个long-standing bug in
Start-Process
unfortunately requires use of embedded double-quoting around arguments that contain spaces, e.g.-ArgumentList '-foo', '"bar baz"'
- see this answer.调用批处理文件 (
.cmd
) 时,命令行被不恰当地解析为好像它是从 内部cmd.exe
[ 提交的=38=] bycmd.exe
,要求包含cmd.exe
个元字符(例如|
的 space-less 个参数为 double-quoted 或者元字符单独为^
-转义。
您可以手动补偿这些行为,如下所示:
$configArgsEscaped =
switch -Regex ($configArgs) {
'[ ^&|<>",;=()]' { '"{0}"' -f ($_ -replace '"', '""') }
default { $_ } # no double-quoting needed
}
现在在 Start-Process
调用中使用 -ArgumentList $configArgsEscaped
代替 -ArgumentList $configArgs
。