当我遇到类似的部署问题时 - 1)用户不知道 powershell 2)我不希望他们必须了解诸如执行策略之类的东西,3)如何启动 PS,4)等等。我将它打包成一批文件。我还想确保有经验的 PS 用户仍然具有 PS 的功能,因此批处理文件确定它是否在 PS 下运行,并在适用的情况下在当前 PS 会话中运行。我从不担心用户会弄乱脚本——如果它“正常工作”他们会很高兴。因此,无论用户喜欢 Explorer、CMD.EXE 还是 PS,它们都得到了满足。
我写的批处理文件首先运行了一些powershell代码来确定批处理文件的进程是否是powershell进程的孙子进程。如果是,则从 PS 调用批处理文件。还会检查执行策略,如果它足够宽松,则使用 Wscript.SendKeys 将击键发送到 PS 以使脚本在当前 PS 会话中运行。如果不是,则它使用 -ExecutionPolicy 参数启动一个新的 PS 会话并将脚本作为命令行参数 (-Command) 传递。
这段 powershell 代码使用返回码与 .CMD 文件进行通信。对不起,它很神秘,但命令行参数的长度是有限的。这是代码:
set scr= $mp=[diagnostics.process]::getcurrentprocess().id
set scr=%scr%; $pp=([wmi]\"win32_process.handle='$mp'\").parentprocessid
set scr=%scr%; $gp=([wmi]\"win32_process.handle='$pp'\").parentprocessid
set scr=%scr%; $ep=[int][microsoft.powershell.executionpolicy](get-executionpolicy)
set scr=%scr%; try {$pnp=1-[int](([wmi]\"win32_process.handle='$gp'\").Name -eq \"powershell.exe\")
set scr=%scr%; } catch {$pnp=1}
set scr=%scr%; $ev = (8 * $pnp + $ep) -band 0xB; %wo% pp: $pp gp: $gp ev: $ev; if ($ev -le 1) {
set scr=%scr% %wo% Launching within existing powershell session...`n;
set scr=%scr% $w=new-object -com wscript.shell;$null=$w.appactivate($gp);
set scr=%scr%; $w.sendkeys(\"^&{{}`$st =cat "%me%";`$sc=`$st -join [char]10 -split 'rem PS script';
set scr=%scr% `$script:myArgs = `\" %*`\";`$sb=[scriptblock]::create{(} `$sc[3]{)};. `$sb{}}~\")
set scr=%scr%; }
set scr=%scr%; exit $ev
powershell -noprofile -Command %scr%
%wo% 是允许调试这个“检查脚本”。如果调试正在进行,则 %wo% 设置为write-host
. 否则,它被设置为定义一个“空”函数,然后调用空函数。null 不执行任何操作,因此不会输出作为函数参数的消息。
注意调用 SendKeys 时的转义。^ 是 CMD.EXE 转义字符,SendKeys 和 PS 一样有自己的转义机制。
如果从 PS 运行,由于 SendKeys,您最终会进入 PS 会话。否则批处理文件会这样做:
set scr= ren function:prompt prompto
set scr=%scr%; function prompt{ 'myApp: '+(prompto)}
set scr=%scr%; $st= (cat %me%) -join \"`n\";
set scr=%scr%; $sx=($st -split 'rem PS script')
set scr=%scr%; $sc=$sx[3]
set scr=%scr%; %wo% myArgs: $myArgs script length: $sc.length
set scr=%scr%; ^&{$script:myArgs=\"%*\"; iex $sc}
title MyApp
rem Change the number of lines on the console if currently set to 25
for /f "tokens=2" %%i in ('mode con^|findstr Lines:') do if %%i LEQ 25 (mode con lines=50&color 5F)
powershell -noexit -noprofile -command "%scr%"
这个“帮助脚本”也不能太长。因此,帮助脚本读取原始 .CMD 文件,然后使用字符串“rem PS script”将其拆分。该字符串将在此帮助程序脚本和批处理文件中(将批处理文件语句与 PS 语句分开)。在我的例子中,字符串也在批处理文件的注释中,所以这就是为什么使用索引 3 的原因。
您的 PS 脚本可以定义函数或模块。您的 PS 脚本还可以输出一些介绍性信息,以向用户解释如何开始、如何获得帮助或任何您想要的。
您的 PS 脚本不仅可以使用 PS 命令行,还可以创建自己的交互环境(例如使用 Read-Host)。但是我不想这样做,因为它会阻止有经验的 PS 用户使用他们对 PS 的知识。例如,如果您的脚本需要用户名/密码,有经验的 PS 用户可以使用 get-credential 创建一个凭据以发送到您的脚本。