当我将参数传递给嵌套的 Start-Process 命令时,PowerShell 删除了多个连续的空格
PowerShell removes multiple consecutive whitespaces when I pass arguments to a nested Start-Process command
此 powershell 代码工作正常:
powershell -NoProfile -Command {Start-Process -FilePath wscript.exe -Verb RunAs -ArgumentList '"C:\Users\TestAccount\Desktop\cartella con spazi\test.vbs" "/CurrentDirectory:C:\Users\TestAccount\Desktop\Cartella con spazi" "/AppData:C:\Users\TestAccount\AppData\Roaming"'}
但是当我输入完整的命令时,powershell 在单词之间只留下一个 space 但在我的路径中有两个连续的:
Start-Process -FilePath powershell.exe -WorkingDirectory "$env:ALLUSERSPROFILE" -Credential $credential -WindowStyle Hidden -ArgumentList "-NoProfile -Command & {Start-Process -FilePath wscript.exe -Verb RunAs -ArgumentList '\`"C:\Users\TestAccount\Desktop\cartella con spazi\test.vbs\`" \`"/CurrentDirectory:C:\Users\TestAccount\Desktop\Cartella con spazi\`" \`"/AppData:C:\Users\TestAccount\AppData\Roaming\`"'}"
同样的错误:
Start-Process -FilePath powershell.exe -WorkingDirectory "$env:ALLUSERSPROFILE" -Credential $credential -WindowStyle Hidden -ArgumentList "-NoProfile -Command Start-Process -FilePath wscript.exe -Verb RunAs -ArgumentList '\`"C:\Users\TestAccount\Desktop\cartella con spazi\test.vbs\`" \`"/CurrentDirectory:C:\Users\TestAccount\Desktop\Cartella con spazi\`" \`"/AppData:C:\Users\TestAccount\AppData\Roaming\`"'"
这是错误信息:
如您所见,在错误消息中路径仅包含一个 space 单词之间,而在代码中我正确输入了两个连续的。
这是输入凭据的代码(就在出现错误的行之前):
$username = 'Username'
$password = 'Password'
$securePassword = ConvertTo-SecureString $password -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential $username, $securePassword
几天来我一直在使用一行代码正常工作,但没有成功。
我输入的那些示例路径实际上是变量(具有不定数量的连续 spaces),因此通过将 -ArgumentList 括在单引号中,变量不会扩展。
你可以采用你认为最合适的策略来帮助我。
Windows10 专业版 64 位
Powershell 版本:5.1.19041.1237(集成于 Windows 10)。
更新:
@mklement0 问题是,实际上有问题的行包含一个可扩展变量:
Start-Process -FilePath powershell.exe -WorkingDirectory "$env:ALLUSERSPROFILE" -Credential $credential -WindowStyle Hidden -ArgumentList @"
-NoProfile -Command "Start-Process -FilePath wscript.exe -Verb RunAs -ArgumentList '\"$PWD\test.vbs\" \"/CurrentDirectory:$PWD\" \"/AppData:$env:APPDATA\"'"
"@
因此,如果路径包含单引号,则该行出错。
更新 2:
@mklement0 自从我将它放入脚本后,您的解决方案已停止工作,这正是我所需要的,并出现以下错误消息:
注意:这个答案解决了Start-Process
问题;对于 不相关的 问题 .ps1
路径中有空格的文件在文件资源管理器中双击时无法 运行,请参阅 this answer .
尝试以下操作,参数传递逐字:
Start-Process `
-FilePath powershell.exe `
-WorkingDirectory "$env:ALLUSERSPROFILE" `
-Credential $credential `
-WindowStyle Hidden `
-ArgumentList @'
-NoProfile -Command "Start-Process -FilePath wscript.exe -Verb RunAs -ArgumentList '\"C:\Users\TestAccount\Desktop\cartella con spazi\test.vbs\" \"/CurrentDirectory:C:\Users\TestAccount\Desktop\Cartella con spazi\" \"/AppData:C:\Users\TestAccount\AppData\Roaming\"'"
'@
-Command
参数包含在 "..."
中,以便按原样保留命令内部空间。
命令字符串中的embedded"
字符转义为\"
外部 -ArgumentList
使用 here-string 来简化嵌入引用。
- 此处使用verbatim形式(
@'<newline>...<newline>'@
);如果您需要字符串插值(嵌入变量引用和表达式的能力),请使用 _expandable 形式 @"<newline>...<newline>"@
- 请参阅下一节。
虽然 PowerShell-内部 `
,反引号用作转义字符,PowerShell 的 CLI 期望 "
个字符在命令行中被\
-转义,以符合最广泛使用的转义约定。
跨平台 PowerShell (Core) 7+ edition 现在也接受 ""
作为 \"
的替代,Windows.[=69 的另一种常见约定=]
虽然您可能在某些情况下必须 结合 两种转义方法,即使用 `\"
以引用 "
first 用于 CLI 和 then 用于 PowerShell 内部使用,在解释 -Command
参数的上下文中,这不是必需的在上面的命令中,因为内部 -ArgumentList
参数使用 单 引号,其中 "
可以按原样使用。
通用解决方案,提供参数通过变量:
假设要使用其值的变量被命名为 $dir1
、$dir2
和 $dir3
(并且它们的值没有嵌入 "
字符。但是 "
无论如何在 Windows 上的文件系统路径中不受支持。
Start-Process `
-FilePath powershell.exe `
-WorkingDirectory "$env:ALLUSERSPROFILE" `
-Credential $credential `
-WindowStyle Hidden `
-ArgumentList @"
-NoProfile -Command "Start-Process -FilePath wscript.exe -Verb RunAs -ArgumentList '\"$($dir1 -replace "'", "''")\" \"/CurrentDirectory:$($dir2 -replace "'", "''")\" \"/AppData:$($dir3 -replace "'", "''")\"'"
"@
此处字符串 (@"<newline>...<newline>"@
) 的 插值 形式现在必须用于外部 -ArgumentList
参数。
由于变量值被插入到 powershell.exe
将被视为单引号字符串 ('...'
) 的内容中,因此变量中嵌入的任何 '
字符值必须转义为 ''
,这就是嵌入式 $(...)
subexpressions 中的 -replace "'", "''"
所做的。
可选阅读:外部程序调用故障排除:
诊断调用外部程序的问题可能很困难,尤其是在这样的嵌套调用中。
准备:创建助手可执行文件echoArgsPause.exe
:
下面创建了一个辅助可执行文件,echoArgsPause.exe
,它回显调用它的完整命令行,以及它已将命令行解析成的各个参数,然后等待击键。
注:
作为基于.NET的*.exe
,它使用.NET的命令行解析规则,而后者又基于Microsoft's C/C++ conventions。不幸的是,并非所有可执行文件都保证使用相同的约定。而且,事实上,WSH CLI(cscript.exe
和 wscript.exe
)不支持参数中的 embedded "
字符。
运行 来自 Windows PowerShell 的代码,因为从 PowerShell (Core) 7.2,不支持通过 Add-Type
创建 可执行文件 。
为了以后重用,将生成的 .\echoArgsPause.exe
文件放在 PATH 的目录中(在 $env:PATH
中列出的目录中)。
要创建不等待击键的变体,请删除 Console.Write("Press a key to exit: ");
和 Console.ReadKey(true);
行并将 -OutputAssembly .\echoArgsPause.exe
更改为 -OutputAssembly ./echoArgs.exe
Add-Type -OutputType ConsoleApplication -OutputAssembly .\echoArgsPause.exe -TypeDefinition @'
using System;
static class ConsoleApp {
static int Main(string[] args) {
Console.WriteLine("\nraw: [{0}]\n", Environment.CommandLine);
for (int i = 0; i < args.Length; ++i) {
Console.WriteLine("arg #{0}: [{1}]", i, args[i]);
}
Console.Write("Press a key to exit: ");
Console.ReadKey(true);
return 0;
}
}
'@
使用 echoArgsPause.exe
解决呼叫问题
- 以下独立示例代码使用您的命令的简化版本,并使用
echoArgsPause.exe
代替您的目标可执行文件 (wscript.exe
) 来打印命令行和解析的参数。
# Specify the full path to the helper executable.
# Here I'm assuming it is in the current dir.
$echoArgsPausePath = "$($PWD.ProviderPath)\echoArgsPause.exe"
# Sample variable values.
$dir1='c:\temp''o 1'
$dir2='c:\temp 2'
$dir3='c:\temp 3'
# Call your command, with echoArgsPause.exe in lieu
# of the target executable (wscript.exe)
Start-Process `
-FilePath powershell.exe `
-WorkingDirectory "$env:ALLUSERSPROFILE" `
-WindowStyle Hidden `
-ArgumentList @"
-NoProfile -Command "Start-Process -FilePath "$echoArgsPausePath" -Verb RunAs -ArgumentList '\"$($dir1 -replace "'", "''")\" \"/CurrentDirectory:$($dir2 -replace "'", "''")\" \"/AppData:$($dir3 -replace "'", "''")\"'"
"@
我找到了另一个没有 Here-String 的解决方案:
$username = 'Username'
$password = 'Password'
$securePassword = ConvertTo-SecureString $password -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential $username, $securePassword
Start-Process -FilePath powershell -WorkingDirectory "$env:ALLUSERSPROFILE" -Credential $credential -WindowStyle Hidden -ArgumentList "-NoProfile -Command `"Start-Process -FilePath wscript -Verb RunAs -ArgumentList '\`"$((Get-Location).Path -replace "'", "''")\test.vbs\`" \`"/CurrentDirectory:$((Get-Location).Path -replace "'", "''")\`" \`"/AppData:$($env:APPDATA -replace "'", "''")\`"'`""
此 powershell 代码工作正常:
powershell -NoProfile -Command {Start-Process -FilePath wscript.exe -Verb RunAs -ArgumentList '"C:\Users\TestAccount\Desktop\cartella con spazi\test.vbs" "/CurrentDirectory:C:\Users\TestAccount\Desktop\Cartella con spazi" "/AppData:C:\Users\TestAccount\AppData\Roaming"'}
但是当我输入完整的命令时,powershell 在单词之间只留下一个 space 但在我的路径中有两个连续的:
Start-Process -FilePath powershell.exe -WorkingDirectory "$env:ALLUSERSPROFILE" -Credential $credential -WindowStyle Hidden -ArgumentList "-NoProfile -Command & {Start-Process -FilePath wscript.exe -Verb RunAs -ArgumentList '\`"C:\Users\TestAccount\Desktop\cartella con spazi\test.vbs\`" \`"/CurrentDirectory:C:\Users\TestAccount\Desktop\Cartella con spazi\`" \`"/AppData:C:\Users\TestAccount\AppData\Roaming\`"'}"
同样的错误:
Start-Process -FilePath powershell.exe -WorkingDirectory "$env:ALLUSERSPROFILE" -Credential $credential -WindowStyle Hidden -ArgumentList "-NoProfile -Command Start-Process -FilePath wscript.exe -Verb RunAs -ArgumentList '\`"C:\Users\TestAccount\Desktop\cartella con spazi\test.vbs\`" \`"/CurrentDirectory:C:\Users\TestAccount\Desktop\Cartella con spazi\`" \`"/AppData:C:\Users\TestAccount\AppData\Roaming\`"'"
这是错误信息:
如您所见,在错误消息中路径仅包含一个 space 单词之间,而在代码中我正确输入了两个连续的。
这是输入凭据的代码(就在出现错误的行之前):
$username = 'Username'
$password = 'Password'
$securePassword = ConvertTo-SecureString $password -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential $username, $securePassword
几天来我一直在使用一行代码正常工作,但没有成功。
我输入的那些示例路径实际上是变量(具有不定数量的连续 spaces),因此通过将 -ArgumentList 括在单引号中,变量不会扩展。
你可以采用你认为最合适的策略来帮助我。
Windows10 专业版 64 位
Powershell 版本:5.1.19041.1237(集成于 Windows 10)。
更新:
@mklement0 问题是,实际上有问题的行包含一个可扩展变量:
Start-Process -FilePath powershell.exe -WorkingDirectory "$env:ALLUSERSPROFILE" -Credential $credential -WindowStyle Hidden -ArgumentList @"
-NoProfile -Command "Start-Process -FilePath wscript.exe -Verb RunAs -ArgumentList '\"$PWD\test.vbs\" \"/CurrentDirectory:$PWD\" \"/AppData:$env:APPDATA\"'"
"@
因此,如果路径包含单引号,则该行出错。
更新 2:
@mklement0 自从我将它放入脚本后,您的解决方案已停止工作,这正是我所需要的,并出现以下错误消息:
注意:这个答案解决了Start-Process
问题;对于 不相关的 问题 .ps1
路径中有空格的文件在文件资源管理器中双击时无法 运行,请参阅 this answer .
尝试以下操作,参数传递逐字:
Start-Process `
-FilePath powershell.exe `
-WorkingDirectory "$env:ALLUSERSPROFILE" `
-Credential $credential `
-WindowStyle Hidden `
-ArgumentList @'
-NoProfile -Command "Start-Process -FilePath wscript.exe -Verb RunAs -ArgumentList '\"C:\Users\TestAccount\Desktop\cartella con spazi\test.vbs\" \"/CurrentDirectory:C:\Users\TestAccount\Desktop\Cartella con spazi\" \"/AppData:C:\Users\TestAccount\AppData\Roaming\"'"
'@
-Command
参数包含在"..."
中,以便按原样保留命令内部空间。命令字符串中的embedded
"
字符转义为\"
外部
-ArgumentList
使用 here-string 来简化嵌入引用。- 此处使用verbatim形式(
@'<newline>...<newline>'@
);如果您需要字符串插值(嵌入变量引用和表达式的能力),请使用 _expandable 形式@"<newline>...<newline>"@
- 请参阅下一节。
- 此处使用verbatim形式(
虽然 PowerShell-内部
`
,反引号用作转义字符,PowerShell 的 CLI 期望"
个字符在命令行中被\
-转义,以符合最广泛使用的转义约定。跨平台 PowerShell (Core) 7+ edition 现在也接受
""
作为\"
的替代,Windows.[=69 的另一种常见约定=]虽然您可能在某些情况下必须 结合 两种转义方法,即使用
`\"
以引用"
first 用于 CLI 和 then 用于 PowerShell 内部使用,在解释-Command
参数的上下文中,这不是必需的在上面的命令中,因为内部-ArgumentList
参数使用 单 引号,其中"
可以按原样使用。
通用解决方案,提供参数通过变量:
假设要使用其值的变量被命名为 $dir1
、$dir2
和 $dir3
(并且它们的值没有嵌入 "
字符。但是 "
无论如何在 Windows 上的文件系统路径中不受支持。
Start-Process `
-FilePath powershell.exe `
-WorkingDirectory "$env:ALLUSERSPROFILE" `
-Credential $credential `
-WindowStyle Hidden `
-ArgumentList @"
-NoProfile -Command "Start-Process -FilePath wscript.exe -Verb RunAs -ArgumentList '\"$($dir1 -replace "'", "''")\" \"/CurrentDirectory:$($dir2 -replace "'", "''")\" \"/AppData:$($dir3 -replace "'", "''")\"'"
"@
此处字符串 (
@"<newline>...<newline>"@
) 的 插值 形式现在必须用于外部-ArgumentList
参数。由于变量值被插入到
powershell.exe
将被视为单引号字符串 ('...'
) 的内容中,因此变量中嵌入的任何'
字符值必须转义为''
,这就是嵌入式$(...)
subexpressions 中的-replace "'", "''"
所做的。
可选阅读:外部程序调用故障排除:
诊断调用外部程序的问题可能很困难,尤其是在这样的嵌套调用中。
准备:创建助手可执行文件echoArgsPause.exe
:
下面创建了一个辅助可执行文件,echoArgsPause.exe
,它回显调用它的完整命令行,以及它已将命令行解析成的各个参数,然后等待击键。
注:
作为基于.NET的
*.exe
,它使用.NET的命令行解析规则,而后者又基于Microsoft's C/C++ conventions。不幸的是,并非所有可执行文件都保证使用相同的约定。而且,事实上,WSH CLI(cscript.exe
和wscript.exe
)不支持参数中的 embedded"
字符。运行 来自 Windows PowerShell 的代码,因为从 PowerShell (Core) 7.2,不支持通过
Add-Type
创建 可执行文件 。为了以后重用,将生成的
.\echoArgsPause.exe
文件放在 PATH 的目录中(在$env:PATH
中列出的目录中)。要创建不等待击键的变体,请删除
Console.Write("Press a key to exit: ");
和Console.ReadKey(true);
行并将-OutputAssembly .\echoArgsPause.exe
更改为-OutputAssembly ./echoArgs.exe
Add-Type -OutputType ConsoleApplication -OutputAssembly .\echoArgsPause.exe -TypeDefinition @'
using System;
static class ConsoleApp {
static int Main(string[] args) {
Console.WriteLine("\nraw: [{0}]\n", Environment.CommandLine);
for (int i = 0; i < args.Length; ++i) {
Console.WriteLine("arg #{0}: [{1}]", i, args[i]);
}
Console.Write("Press a key to exit: ");
Console.ReadKey(true);
return 0;
}
}
'@
使用 echoArgsPause.exe
解决呼叫问题
- 以下独立示例代码使用您的命令的简化版本,并使用
echoArgsPause.exe
代替您的目标可执行文件 (wscript.exe
) 来打印命令行和解析的参数。
# Specify the full path to the helper executable.
# Here I'm assuming it is in the current dir.
$echoArgsPausePath = "$($PWD.ProviderPath)\echoArgsPause.exe"
# Sample variable values.
$dir1='c:\temp''o 1'
$dir2='c:\temp 2'
$dir3='c:\temp 3'
# Call your command, with echoArgsPause.exe in lieu
# of the target executable (wscript.exe)
Start-Process `
-FilePath powershell.exe `
-WorkingDirectory "$env:ALLUSERSPROFILE" `
-WindowStyle Hidden `
-ArgumentList @"
-NoProfile -Command "Start-Process -FilePath "$echoArgsPausePath" -Verb RunAs -ArgumentList '\"$($dir1 -replace "'", "''")\" \"/CurrentDirectory:$($dir2 -replace "'", "''")\" \"/AppData:$($dir3 -replace "'", "''")\"'"
"@
我找到了另一个没有 Here-String 的解决方案:
$username = 'Username'
$password = 'Password'
$securePassword = ConvertTo-SecureString $password -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential $username, $securePassword
Start-Process -FilePath powershell -WorkingDirectory "$env:ALLUSERSPROFILE" -Credential $credential -WindowStyle Hidden -ArgumentList "-NoProfile -Command `"Start-Process -FilePath wscript -Verb RunAs -ArgumentList '\`"$((Get-Location).Path -replace "'", "''")\test.vbs\`" \`"/CurrentDirectory:$((Get-Location).Path -replace "'", "''")\`" \`"/AppData:$($env:APPDATA -replace "'", "''")\`"'`""