如何调试混合 cmd 和 powershell 脚本以解决特殊字符问题,如 & 和
Posted
技术标签:
【中文标题】如何调试混合 cmd 和 powershell 脚本以解决特殊字符问题,如 & 和【英文标题】:how to debug mixed cmd and powershell scripts to solve special character problems like ampersand & 【发布时间】:2021-11-24 05:26:47 【问题描述】:我正在创建一个 dos 批处理脚本 (cmd.exe) .cmd
,它通过 powershell 将自己提升为管理员。
但如果它存储在名称中同时包含空格和 & 符号的文件夹中,它就不起作用了。
我不是专家,所以我搜索了网络,但找不到解决问题的解决方案或文档。所以我也想知道如何调试这种特殊情况和问题的可能解决方案,以及在哪里可以找到描述解决方案的文档或到达那里的步骤..(如果有的话)。
重现问题:我在桌面下创建了一个名为“Pie & tea”的文件夹,没有引号。在文件夹中,我创建了一个脚本“test.cmd”,在其中我只放了重现问题的必要条件。这是脚本的内容:
@echo off
pause
PowerShell start "%~f0" -verb runas
pause
goto: eof
我通过双击来运行脚本(这是所需的模式)。此代码显示错误(我使用翻译器从意大利语翻译):
Press any key to continue. . .
In row: 1 car: 34
+ start C:\Users\fposc\Desktop\Pie & tea\test.cmd -verb runas
+ ~
Ampersand (&) not allowed. The & operator is reserved for future use. Use "&" to pass the ampersand as
string.
+ CategoryInfo: ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId: AmpersandNotAllowed
Press any key to continue. . .
并按照一些说明,我通过将双引号增加了三倍,使用 powershell 修改了代码行:
PowerShell start """%~f0""" -verb runas
但是 cmd 窗口打开,在按下空格后 UAC 启动并在同意后另一个窗口出现了一会儿,但它立即关闭,我看不到任何错误。
我在网上看到任何使用单引号'
或powershell 转义字符`
或dos 批处理的插入符号^
或powershell 的--%
参数等的解决方案。
但我无法解决。
我没有把所做的尝试放在这里,因为我以一种愚蠢的方式复制(因为我不熟悉混合 cmd / powershell 操作)并且我结合各种解决方案进行了无数次随机尝试。没有任何积极的结果。
我还看到了我想评估的解决方案,例如:
-
更改目录并使用相对路径。
准备字符串变量并替换字符。
使用 powershell -encodedcommand 传递参数。
但首选的解决方案是解释如何在单行代码
PowerShell start "%~f0" -verb runas
上转义字符以及如何到达那里以及参考文档。
访问的一些链接:
VBS Shell.Application and PowerShell Start-Process do not escape ampersands internally
Quoting issues with PowerShell
Issues on windows when the username contains &
Executing PowerShell script via explorer context menu on items containing ampersands in their names
***: batch file deal with Ampersand (&) in folder name
***: How to access file paths in PowerShell containing special characters
***: How to pass strings to powershell from cmd with ampersands in values?
【问题讨论】:
使用-File
参数后是否还有同样的效果? PowerShell -File "%~f0"
@Abraham Zinala。我试过了,但 -File
参数说它只接受 powershell .ps1 脚本。该脚本是一个 .cmd,并给出错误:Unable to process -File 'C:\Users\fposc\Desktop\Pie & tea\test.cmd'. The file has no 'ps1' extension. Please provide a valid Windows PowerShell script file name and try again.
我不知道如何使用-File
提升
为什么要用cmd启动powershell来启动.bat文件?
@Theo,因为我上面写的脚本是用dos批处理写的。
.cmd 脚本检查是否被提升。如果没有提升,它会通过 powershell 自动提升。
【参考方案1】:
调试提示:
如果 PowerShell 端出现问题,您将在调用 cmd.exe
会话中看到错误消息;要打印 PowerShell 本身看到的命令行,请将 [Environment]::CommandLine;
添加到正在传递的命令之前。[1]
要使 PowerShell 的 Start-Process
cmdlet(其内置别名为 start
)在 保持会话打开的 cmd.exe
进程中启动批处理文件,请显式调用 cmd.exe
,使用cmd /k
,并将批处理文件路径传递给它。一旦您确定一切都按预期工作,请将 /k
替换为 /c
(创建一个在批处理文件终止时终止的会话)。
您的主要问题:
很遗憾,如果批处理文件路径包含 &
字符,cmd.exe
中的错误需要 手动 ^
-escaping 或 &
作为空格,可能还有其他cmd.exe
元字符,即使路径整体包含在"..."
中; 此外,似乎完整批处理文件路径必须在调用时使用(%~f0
确保):
例如,要调用C:\foo\a & b.cmd
,cmd /k "C:\foo\a & b.cmd"
是不够 - 你必须使用cmd /k "C:\foo\a^ ^&^ b.cmd"
下面的命令使用-replace
operation 在 PowerShell 代码中执行此^
-转义,对%~f0
的值,cmd.exe
扩展为批处理文件的完整路径。
以下解决方案:
使用辅助。环境变量使其在大多数情况下都能正常工作,即使路径包含其他奇异字符,例如$
、%
和 '
。
还支持在调用时传递参数。
请参阅底部部分了解解决方案的演变,其中包括详细说明。
@echo off
:: Save the full path of this batch file in an aux. environment variable.
set "__THISFILE=%~f0"
:: Save the arguments received in an aux. environment variable.
:: Note: The space after "=" is required for the PowerShell command
:: to also work if *no* arguments were passed.
set __ARGS= %*
:: Unless already elevated, relaunch this batch file,
:: with arguments passed through.
:: Note: Replace `exit /b` with just `exit` to
:: close the current console window after relaunching.
net session >NUL 2>NUL || (powershell.exe -noprofile -c "Start-Process -Verb RunAs cmd /k, ($env:__THISFILE -replace '[ &%%^]', '^$&'), $env:__ARGS" & exit /b)
:: Reset the aux. variables.
set __ARGS=
set __THISFILE=
echo Now running with elevation. Arguments received:
echo. %*
上述解决方案的演变:
如果您的批处理文件路径不包含 '
字符:
嵌入的'...'
引用在下面的PowerShell命令中使用,以避免转义嵌入的"
引号,这可能会变得棘手;这假设批处理文件路径本身不包含 '
字符(如果您的路径确实包含 '
字符,请参见下文。)
@echo off
:: Unless already elevated, relaunch this batch file with elevation.
net session >NUL 2>NUL || (powershell.exe -c "Start-Process -Verb RunAs cmd /k, ('%~f0' -replace '[ &]', '^$&')" & goto :eof)
echo Now running with elevation.
注意:
net session >NUL 2>NUL
是一个简单的技巧,用于测试当前进程是否已提升(以管理员身份运行):它仅在提升的会话中成功(反映在退出代码 0
中,||
和 @987654362 @ 运算符隐式作用)。
-c
(-Command
) 明确表示正在将命令传递给powershell.exe
;虽然不是绝对必要,但养成一个好习惯,因为pwsh.exe
,PowerShell (Core) 7+ CLI,现在需要它(请参阅链接的 CLI下面的文档)。
/k, ...
创建一个 array 参数,这些参数按位置传递给Start-Process
的-ArgumentList
参数 - 本质上,Start-Process
从可执行文件名称构建一个命令行,@ 987654371@,所有参数都由字符串连接并在后台使用空格 - 有关详细信息,请参阅您的后续问题的this answer。
如果您的批处理文件路径包含 '
字符,则必须使用嵌入的 "..."
引用:
使用powershell.exe
,Windows PowerShell CLI,最强大的方法 - 这里确实需要 - 使用"^""
(原文如此)来转义每个嵌入"
,如下图。
(相比之下,pwsh.exe
,PowerShell (Core) 7+ CLI 现在幸运地接受 ""
。)
net session >NUL 2>NUL || (powershell.exe -nop -c "Start-Process -Verb RunAs cmd /k, ("^""%~f0"^"" -replace '[ &]', '^$&')" & goto :eof)
如果您的批处理文件路径包含 $
字符。 和 '
chars.,一个辅助。必须使用环境变量:
$
字符。如果您使用嵌入式'...'
引用,单独使用不会有问题,但如果'
也存在,则嵌入"..."
引用不是一个选项,因为PowerShell 会处理$
字符。作为变量引用的开始。
查看顶部的解决方案。
如果您还想支持将参数传递给批处理文件:
注意:顶部的解决方案现在采用了这种方法,同时还处理了更广泛的奇异批处理文件路径。
为了防止进一步引用令人头疼的问题,辅助。环境变量,__ARGS
在下面的代码中用于存储批处理文件接收到的所有参数 (%*
),然后 PowerShell 代码可以在重新调用时通过 $env:__ARGS
访问这些参数。
@echo off
:: Save the arguments received in an aux. environment variable.
:: Note: The space after "=" is required for the PowerShell command
:: to also work if *no* arguments were passed.
set __ARGS= %*
:: Unless already elevated, relaunch this batch file,
:: with arguments passed through.
net session >NUL 2>NUL || (powershell.exe -nop -c "Start-Process -Verb RunAs cmd /k, ("^""%~f0"^"" -replace '[ &]', '^$&'), $env:__ARGS" & goto :eof)
:: Reset the aux. variable.
set __ARGS=
echo Now running with elevation. Arguments received:
echo. %*
[1] 一个简单的例子(来自cmd.exe
的调用):powershell -c "[Environment]::CommandLine; '%OS%'"
【讨论】:
以上是关于如何调试混合 cmd 和 powershell 脚本以解决特殊字符问题,如 & 和的主要内容,如果未能解决你的问题,请参考以下文章
我无法在 CMD 和 powershell 中执行 C 程序