如何在Windows批处理文件中运行PowerShell脚本
如何将PowerShell脚本嵌入到与Windows批处理脚本相同的文件中 我知道这种情况在其他情况下也是可能的:如何在Windows批处理文件中运行PowerShell脚本,windows,powershell,batch-file,cmd,scripting,Windows,Powershell,Batch File,Cmd,Scripting,如何将PowerShell脚本嵌入到与Windows批处理脚本相同的文件中 我知道这种情况在其他情况下也是可能的: 使用sqlcmd在批处理脚本中嵌入SQL,并在文件开头巧妙地安排goto和注释 在*nix环境中,脚本的第一行注释掉了要运行脚本的程序的名称,例如,#/usr/local/bin/python 可能没有办法做到这一点-在这种情况下,我将不得不从启动脚本中调用单独的PowerShell脚本 我考虑过的一个可能的解决方案是回显PowerShell脚本,然后运行它。不这样做的一个很好
- 使用
在批处理脚本中嵌入SQL,并在文件开头巧妙地安排goto和注释sqlcmd
- 在*nix环境中,脚本的第一行注释掉了要运行脚本的程序的名称,例如,
#/usr/local/bin/python
- 一行末尾的克拉是一个延续,就像Visual Basic中的下划线一样
- 符号
通常用于分隔命令&
在单独的行上产生两个回音echo Hello&echo World
- %0将为您提供当前正在运行的脚本
- 它不起作用。。。因为
- 该文件的扩展名不符合PowerShell的要求:
Windows PowerShell控制台文件“insideout.bat”的扩展名不是psc1。Windows PowerShell控制台文件扩展名必须为psc1。
- CMD也并不完全满意这种情况——尽管它偶然发现了
“#”,但它并没有被识别为内部或外部命令、可操作程序或批处理文件。
- 如果不完全理解您的问题,我的建议如下:
@echo off
set MYSCRIPT="some cool powershell code"
powershell -c %MYSCRIPT%
或者更好
@echo off
set MYSCRIPTPATH=c:\work\bin\powershellscript.ps1
powershell %MYSCRIPTPATH%
如果您不介意PowerShell开始时出现一个错误,那么这似乎是可行的:
dosps.cmd
:
@powershell -<%~f0&goto:eof
Write-Output "Hello World"
Write-Output "Hello World again"
@findstr/v "^@f.*&" "%~f0"|powershell -&goto:eof
Write-Output "Hello World"
Write-Output "Hello some@com & again"
@powershell-此命令仅将正确的行传递给powershell:
dosps2.cmd
:
@powershell -<%~f0&goto:eof
Write-Output "Hello World"
Write-Output "Hello World again"
@findstr/v "^@f.*&" "%~f0"|powershell -&goto:eof
Write-Output "Hello World"
Write-Output "Hello some@com & again"
排除以@f
开头并包含&
的行,并将所有其他内容传递给PowerShell
C:\tmp>dosps2
Hello World
Hello some@com & again
听起来你在寻找有时被称为“多语言文字”的东西。对于CMD->PowerShell
@@:: This prolog allows a PowerShell script to be embedded in a .CMD file.
@@:: Any non-PowerShell content must be preceeded by "@@"
@@setlocal
@@set POWERSHELL_BAT_ARGS=%*
@@if defined POWERSHELL_BAT_ARGS set POWERSHELL_BAT_ARGS=%POWERSHELL_BAT_ARGS:"=\"%
@@PowerShell -Command Invoke-Expression $('$args=@(^&{$args} %POWERSHELL_BAT_ARGS%);'+[String]::Join([char]10,$((Get-Content '%~f0') -notmatch '^^@@'))) & goto :EOF
如果不需要支持引用的参数,甚至可以将其设置为一行:
@PowerShell -Command Invoke-Expression $('$args=@(^&{$args} %*);'+[String]::Join([char]10,(Get-Content '%~f0') -notmatch '^^@PowerShell.*EOF$')) & goto :EOF
摘自。那是PowerShell v1;在v2中可能更简单,但我没有看过。这支持参数,不像Carlos发布的解决方案,也不像Jay发布的解决方案那样中断多行命令或使用param
。唯一的缺点是这个解决方案会创建一个临时文件。对于我的用例,这是可以接受的
@@echo off
@@findstr/v "^@@.*" "%~f0" > "%~f0.ps1" & powershell -ExecutionPolicy ByPass "%~f0.ps1" %* & del "%~f0.ps1" & goto:eof
这个话题已经讨论过了。主要目标是避免使用临时文件,以减少缓慢的I/O操作,并在没有冗余输出的情况下运行脚本
这是我认为最好的解决方案:
<# :
@echo off
setlocal
set "POWERSHELL_BAT_ARGS=%*"
if defined POWERSHELL_BAT_ARGS set "POWERSHELL_BAT_ARGS=%POWERSHELL_BAT_ARGS:"=\"%"
endlocal & powershell -NoLogo -NoProfile -Command "$input | &{ [ScriptBlock]::Create( ( Get-Content \"%~f0\" ) -join [char]10 ).Invoke( @( &{ $args } %POWERSHELL_BAT_ARGS% ) ) }"
goto :EOF
#>
param(
[string]$str
);
$VAR = "Hello, world!";
function F1() {
$str;
$script:VAR;
}
F1;
param(
[字符串]$str
);
$VAR=“你好,世界!”;
函数F1(){
$str;
$script:VAR;
}
F1;
更好的方法(见):
$VAR=“----来自POWERSHELL”;
$VAR;
$POWERSHELL\u BAT\u ARGS=$env:POWERSHELL\u BAT\u ARGS
$POWERSHELL\u BAT\u ARGS
其中POWERSHELL\u BAT\u ARGS
是批处理部分中首先设置为变量的命令行参数
技巧在批次重定向优先级-这行<代码>
也考虑,其中<强>支持嵌入式PuthSeBand和/或VBScript /JScript COD<强/E;这篇文章是作者本人在2013年发布的文章改编而成,但由于只是一个链接式的答案,它在2015年被删除。
一种改进了以下方面的解决方案:
创建包含以下内容的批处理文件(例如sample.cmd
):
<# ::
@echo off & setlocal
copy /y "%~f0" "%TEMP%\%~n0.ps1" >NUL && powershell -NoProfile -ExecutionPolicy Bypass -File "%TEMP%\%~n0.ps1" %*
set ec=%ERRORLEVEL% & del "%TEMP%\%~n0.ps1"
exit /b %ec%
#>
# Paste arbitrary PowerShell code here.
# In this example, all arguments are echoed.
'Args:'
$Args | % { 'arg #{0}: [{1}]' -f ++$i, $_ }
产生如下结果:
Args:
arg#1:[值w/空格和特殊字符。(\|'),在Windows NT上]
arg#2:[666]
arg#3:[丽莎“左眼”洛佩兹]
注意如何将嵌入的“
字符作为\”
传递
但是,有些边缘案例与嵌入式“
字符相关。:
:: # BREAKS, due to the `&` inside \"...\"
sample.cmd "A \"rock & roll\" life style"
:: # Doesn't break, but DOESN'T PRESERVE ARGUMENT BOUNDARIES.
sample.cmd "A \""rock & roll\"" life style"
这些困难都是由于cmd.exe
的有缺陷的参数解析造成的,最终,试图隐藏这些缺陷是毫无意义的,如图所示
正如他所解释的,在\\“…\”
序列中使用^^
(sic)转义以下cmd.exe
元字符可以解决问题:
& | < >
我目前对该任务的偏好是一个polyglot头,其工作方式与:
由于cmd.exe
的参数解析毫无价值,这一点确实被打破了。我通常发现,我试图隐藏cmd的许多限制的工作越少,导致自己出现的意外错误就越少(例如,我确信我可以想出包含括号的参数,这些参数将打破mklement0在其他方面无可挑剔的符号和转义逻辑)。在我看来,咬紧牙关,使用类似
sample.cmd "A \"rock ^^^& roll\" life style"
第一个和第三个^
转义在最初解析该命令行时被吃掉;第二个幸存下来,以转义嵌入在命令行中的&
<# :cmd header for PowerShell script
@ set dir=%~dp0
@ set ps1="%TMP%\%~n0-%RANDOM%-%RANDOM%-%RANDOM%-%RANDOM%.ps1"
@ copy /b /y "%~f0" %ps1% >nul
@ powershell -NoProfile -ExecutionPolicy Bypass -File %ps1% %*
@ del /f %ps1%
@ goto :eof
#>
# Paste arbitrary PowerShell code here.
# In this example, all arguments are echoed.
$Args | % { 'arg #{0}: [{1}]' -f ++$i, $_ }
# BREAKS, due to the `&` inside \"...\"
sample.cmd "A \"rock & roll\" life style"
sample.cmd "A \"rock ^^^& roll\" life style"
<# :# PowerShell comment protecting the Batch section
@echo off
:# Disabling argument expansion avoids issues with ! in arguments.
setlocal EnableExtensions DisableDelayedExpansion
:# Prepare the batch arguments, so that PowerShell parses them correctly
set ARGS=%*
if defined ARGS set ARGS=%ARGS:"=\"%
if defined ARGS set ARGS=%ARGS:'=''%
:# The ^ before the first " ensures that the Batch parser does not enter quoted mode
:# there, but that it enters and exits quoted mode for every subsequent pair of ".
:# This in turn protects the possible special chars & | < > within quoted arguments.
:# Then the \ before each pair of " ensures that PowerShell's C command line parser
:# considers these pairs as part of the first and only argument following -c.
:# Cherry on the cake, it's possible to pass a " to PS by entering two "" in the bat args.
echo In Batch
PowerShell -c ^"Invoke-Expression ('^& {' + [io.file]::ReadAllText(\"%~f0\") + '} %ARGS%')"
echo Back in Batch. PowerShell exit code = %ERRORLEVEL%
exit /b
###############################################################################
End of the PS comment around the Batch section; Begin the PowerShell section #>
echo "In PowerShell"
$Args | % { "PowerShell Args[{0}] = '$_'" -f $i++ }
exit 0
PowerShell -c ^"Invoke-Expression ('^& {' + [io.file]::ReadAllText('%~f0') + '} %ARGS%')"
PowerShell -c ^"Invoke-Expression ('^& {' + (get-content -raw '%~f0') + '} %ARGS%')"
<# :# PowerShell comment protecting the Batch section
@echo off
:# Disabling argument expansion avoids issues with ! in arguments.
setlocal EnableExtensions DisableDelayedExpansion
:# Prepare the batch arguments, so that PowerShell parses them correctly
set ARGS=%*
if defined ARGS set ARGS=%ARGS:"=\"%
if defined ARGS set ARGS=%ARGS:'=''%
:# The ^ before the first " ensures that the Batch parser does not enter quoted mode
:# there, but that it enters and exits quoted mode for every subsequent pair of ".
:# This in turn protects the possible special chars & | < > within quoted arguments.
:# Then the \ before each pair of " ensures that PowerShell's C command line parser
:# considers these pairs as part of the first and only argument following -c.
:# Cherry on the cake, it's possible to pass a " to PS by entering two "" in the bat args.
echo In Batch
PowerShell -c ^"Invoke-Expression ('^& {' + (get-content -raw '%~f0') + '} %ARGS%')"
echo Back in Batch. PowerShell exit code = %ERRORLEVEL%
exit /b
###############################################################################
End of the PS comment around the Batch section; Begin the PowerShell section #>
echo "In PowerShell"
$Args | % { "PowerShell Args[{0}] = '$_'" -f $i++ }
exit 0