如何使用批处理文件使PowerShell脚本更易于运行
For several reasons, mostly security-related, PowerShell scripts aren’t as easily portable and usable as batch scripts can be. However, we can bundle a batch script with our PowerShell scripts to work around these issues. Here, we’ll show you a few of those problem areas, and how to build a batch script to get around them.
由于多种原因,PowerShell脚本与批处理脚本一样不容易移植和使用,而这些原因大多与安全性相关,所以它们很容易。 但是,我们可以将批处理脚本与PowerShell脚本捆绑在一起,以解决这些问题。 在这里,我们将向您展示其中一些问题领域,以及如何构建批处理脚本来解决这些问题。
为什么我不能只将.PS1文件复制到另一台计算机上并运行它? (Why can’t I just copy my .PS1 file to another computer and run it?)
Unless the target system has been pre-configured to allow running of arbitrary scripts, with the required privileges, and using the right settings, chances are you’re going to run into some problems when you try to do this.
除非已将目标系统预先配置为允许运行具有所需权限的任意脚本,并使用正确的设置,否则尝试执行此操作时可能会遇到一些问题。
-
PowerShell is not associated to the .PS1 file extension by default.We brought this up initially in our PowerShell Geek School series. Windows associates .PS1 files to Notepad by default, instead of sending them to the PowerShell command interpreter. This is to prevent accidental execution of malicious scripts by simply double-clicking them. There are ways you can change this behavior, but it’s probably not something you want to do on every computer you’re carrying your scripts around to – especially if some of those computers aren’t your own.
默认情况下,PowerShell未与.PS1文件扩展名关联。 我们最初在PowerShell Geek School系列中提出了这一点。 Windows默认将.PS1文件关联到记事本,而不是将它们发送到PowerShell命令解释器。 这是为了防止恶意脚本的意外执行,只需双击它们即可。 您可以通过多种方法来更改此行为,但是可能并不是要在携带脚本的每台计算机上都执行此操作,尤其是其中有些不是您自己的计算机时。
-
PowerShell does not allow external script execution by default.The ExecutionPolicy setting in PowerShell prevents execution of external scripts by default in all versions of Windows. In some Windows versions, the default doesn’t allow script execution at all. We showed you how to change this setting in How to Allow the Execution of PowerShell Scripts on Windows 7. However, this is also something you don’t want to do on just any computer.
默认情况下,PowerShell不允许外部脚本执行。 PowerShell中的ExecutionPolicy设置默认情况下在所有Windows版本中都阻止执行外部脚本。 在某些Windows版本中,默认设置根本不允许脚本执行。 我们在“ 如何在Windows 7上允许执行PowerShell脚本”中向您展示了如何更改此设置。 但是,这也是您不希望在任何计算机上执行的操作。
-
Some PowerShell scripts won’t work without Administrator permissions.Even running with an Administrator-level account, you still need to get through User Account Control (UAC) to perform certain actions. We don’t want to disable this, but it’s still nice when we can make it a bit easier to deal with.
没有管理员权限,某些PowerShell脚本将无法工作。 即使使用管理员级别的帐户运行,您仍需要通过用户帐户控制(UAC)来执行某些操作。 我们不想禁用它 ,但是当我们可以使它更容易处理时,它仍然很好。
-
Some users may have customized PowerShell environments.
一些用户可能具有自定义的PowerShell环境。
You probably won’t run into this often, but when you do it can make running and troubleshooting your scripts a bit frustrating. Fortunately, we can get around this without making any permanent changes as well.
您可能不会经常遇到这种情况,但是当您这样做时,可能会使脚本的运行和故障排除有点令人沮丧。 幸运的是,我们无需进行任何永久性更改即可解决此问题。
步骤1:双击运行。 (Step 1: Double-click to run.)
Let’s start by addressing the first problem – .PS1 file associations. You can’t double-click to run .PS1 files, but you can execute a .BAT file that way. So, we’ll write a batch file to call the PowerShell script from the command line for us.
让我们首先解决第一个问题-.PS1文件关联。 您不能双击运行.PS1文件,但可以以这种方式执行.BAT文件。 因此,我们将为我们编写一个批处理文件以从命令行调用PowerShell脚本。
So we don’t have to re-write the batch file for every script, or every time we move a script around, it’s going to make use of a self-referencing variable to build the file path for the PowerShell script. To make this work, the batch file will need to be placed in the same folder as your PowerShell script and have the same file name. So if your PowerShell script is called “MyScript.ps1”, you’ll want to name your batch file “MyScript.bat” and make sure it’s in the same folder. Then, put these lines in the batch script:
因此,我们不必为每个脚本重新编写批处理文件,也不必每次移动脚本时都将使用自引用变量来为PowerShell脚本构建文件路径。 为了使此工作有效,批处理文件将需要与PowerShell脚本放在同一文件夹中,并具有相同的文件名。 因此,如果您的PowerShell脚本名为“ MyScript.ps1”,则需要将批处理文件命名为“ MyScript.bat”,并确保它位于同一文件夹中。 然后,将这些行放入批处理脚本中:
@ECHO OFF PowerShell.exe -Command "& '%~dpn0.ps1'" PAUSE
@ECHO OFF PowerShell.exe -Command "& '%~dpn0.ps1'" PAUSE
If it weren’t for the other security restrictions in place, that would really be all it takes to run a PowerShell script from a batch file. In fact, the first and last lines are mainly just a matter of preference – it’s the second line that’s really doing the work. Here’s the breakdown:
如果没有其他安全限制,那么实际上就是从批处理文件运行PowerShell脚本所需的全部。 实际上,第一行和最后一行主要只是一个优先事项-这实际上是第二行。 细目如下:
@ECHO OFF turns off command echoing. This just keeps your other commands from showing on-screen when the batch file runs. This line is itself hidden by the use of the at (@) symbol in front of it.
@ECHO OFF关闭命令回显。 当批处理文件运行时,这只是使您的其他命令无法在屏幕上显示。 该行本身通过使用前面的at(@)符号隐藏。
PowerShell.exe -Command “& ‘%~dpn0.ps1′” actually runs the PowerShell script. PowerShell.exe can of course be called from any CMD window or batch file to launch PowerShell to a bare console like usual. You can also use it to run commands straight from a batch file, by including the -Command parameter and appropriate arguments. The way this is used to target our .PS1 file is with the special %~dpn0 variable. Run from a batch file, %~dpn0 evaluates to the drive letter, folder path, and file name (without extension) of the batch file. Since the batch file and PowerShell script will be in the same folder and have the same name, %~dpn0.ps1 will translate to the full file path of the PowerShell script.
PowerShell.exe-命令“&'%〜dpn0.ps1'”实际上运行PowerShell脚本。 当然,可以从任何CMD窗口或批处理文件中调用PowerShell.exe,以将PowerShell启动到像通常一样的裸控制台。 您还可以通过包含-Command参数和适当的参数,使用它直接从批处理文件运行命令。 用于定位.PS1文件的方法是使用特殊的%〜dpn0变量。 从批处理文件运行,%〜dpn0评估为该批处理文件的驱动器号,文件夹路径和文件名(不带扩展名)。 由于批处理文件和PowerShell脚本将位于同一文件夹中且具有相同的名称,因此%〜dpn0.ps1将转换为PowerShell脚本的完整文件路径。
PAUSE just pauses the batch execution and waits for user input. This is generally useful to have at the end of your batch files, so that you have a chance to review any command output before the window disappears. As we go through testing of each step, the usefulness of this will become more obvious.
PAUSE只是暂停批处理执行并等待用户输入。 通常,将其放在批处理文件的末尾很有用,这样您就有机会在窗口消失之前查看所有命令输出。 随着我们对每个步骤进行测试,其用途将变得更加明显。
So, the basic batch file is set up. For demonstration purposes, this file is saved as “D:\Script Lab\MyScript.bat” and there’s a “MyScript.ps1” in the same folder. Let’s see what happens when we double-click MyScript.bat.
这样就建立了基本的批处理文件。 出于演示目的,此文件另存为“ D:\ Script Lab \ MyScript.bat”,并且在同一文件夹中有一个“ MyScript.ps1”。 让我们看看双击MyScript.bat会发生什么。
Obviously the PowerShell script didn’t run, but that’s to be expected – we’ve only addressed the first of our four problems, after all. However, there are some important bits demonstrated here:
显然,PowerShell脚本没有运行,但这是可以预期的-毕竟,我们仅解决了四个问题中的第一个。 但是,这里展示了一些重要的内容:
- The window title shows that the batch script successfully launched PowerShell. 窗口标题显示批处理脚本已成功启动PowerShell。
- The first line of output shows that a custom PowerShell profile is in use. This is potential problem #4, listed above. 输出的第一行显示正在使用自定义PowerShell配置文件。 这是上面列出的潜在问题4。
- The error message demonstrates ExecutionPolicy restrictions in effect. That’s our problem #2. 该错误消息演示了有效的ExecutionPolicy限制。 那就是我们的问题2。
- The underlined part of the error message (which is done natively by PowerShell’s error output) shows the batch script was correctly targeting the intended PowerShell script (D:\Script Lab\MyScript.ps1). So we at least know that much is working properly. 错误消息的带下划线的部分(由PowerShell的错误输出以本机方式完成)显示批处理脚本已正确针对目标的PowerShell脚本(D:\ Script Lab \ MyScript.ps1)。 因此,我们至少知道很多工作正常。
The profile, in this case, is a simple one-line script used for this demonstration to generate output whenever the profile is active. You can customize your own PowerShell profile to do this too, if you want to test these scripts yourself. Simply add the following line to your profile script:
在这种情况下,配置文件是一个简单的单行脚本,用于此演示,以在配置文件处于活动状态时生成输出。 如果要自己测试这些脚本,也可以自定义自己的PowerShell配置文件 。 只需将以下行添加到您的配置文件脚本中:
Write-Output 'Custom PowerShell profile in effect!'
The ExecutionPolicy on the test system here is set to RemoteSigned. This allows execution of scripts created locally (like the profile script), while blocking scripts from outside sources unless they’re signed by a trusted authority. For demonstration purposes, the following command was used to flag MyScript.ps1 as being from an external source:
测试系统上的ExecutionPolicy此处设置为RemoteSigned。 这允许执行本地创建的脚本(例如配置文件脚本),同时阻止来自外部源的脚本,除非它们由受信任的权威机构签名。 出于演示目的,使用以下命令将MyScript.ps1标记为来自外部源:
Add-Content -Path 'D:\Script Lab\MyScript.ps1' -Value "[ZoneTransfer]`nZoneId=3" -Stream 'Zone.Identifier'
Add-Content -Path 'D:\Script Lab\MyScript.ps1' -Value "[ZoneTransfer]`nZoneId=3" -Stream 'Zone.Identifier'
That sets the Zone.Identifier alternate data stream on MyScript.ps1 so that Windows will think the file came from the Internet. It can be easily reversed with the following command:
这将在MyScript.ps1上设置Zone.Identifier备用数据流,以便Windows认为该文件来自Internet 。 使用以下命令可以很容易地将其反转:
Clear-Content -Path 'D:\Script Lab\MyScript.ps1' -Stream 'Zone.Identifier'
步骤2:解决ExecutionPolicy。 (Step 2: Getting around ExecutionPolicy.)
Getting around the ExecutionPolicy setting, from CMD or a batch script, is actually pretty easy. We just modify the second line of the script to add one more parameter to the PowerShell.exe command.
实际上,从CMD或批处理脚本避开ExecutionPolicy设置非常容易。 我们只需要修改脚本的第二行,即可在PowerShell.exe命令中添加另一个参数。
PowerShell.exe -ExecutionPolicy Bypass -Command "& '%~dpn0.ps1'"
PowerShell.exe -ExecutionPolicy Bypass -Command "& '%~dpn0.ps1'"
The -ExecutionPolicy parameter can be used to modify the ExecutionPolicy that is used when you spawn a new PowerShell session. This will not persist beyond that session, so we can run PowerShell like this whenever we need without weakening the general security posture of the system. Now that we’ve fixed that, let’s have another go at it:
-ExecutionPolicy参数可用于修改生成新的PowerShell会话时使用的ExecutionPolicy。 这将不会在该会话之后持续下去,因此我们可以在需要时像这样运行PowerShell,而不会削弱系统的总体安全状态。 现在,我们已经解决了这一问题,让我们再来解决一下:
Now that the script has properly executed, we can see what it actually does. It’s letting us know that we’re running the script as a Limited user. The script is in fact being run by an account with Administrator permissions, but User Account Control is getting in the way. Though details of how the script is checking for Administrator access are beyond the scope of this article, here’s the code that’s being used for demonstration:
现在脚本已正确执行,我们可以看到它的实际作用。 让我们知道我们以受限用户身份运行脚本。 该脚本实际上是由具有管理员权限的帐户运行的,但是“用户帐户控制”已成为问题。 尽管有关脚本如何检查管理员访问权限的详细信息不在本文讨论范围之内,但以下是用于演示的代码:
if (([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {Write-Output 'Running as Administrator!'} else {Write-Output 'Running Limited!'} Pause
You’ll also notice that there’s now two “Pause” operations in the script output – one from the PowerShell script, and one from the batch file. The reason for this will be more apparent in the next step.
您还将注意到,脚本输出中现在有两个“暂停”操作–一个来自PowerShell脚本,另一个来自批处理文件。 下一步的原因将更加明显。
步骤3:获得管理员访问权限。 (Step 3: Getting Administrator access.)
If your script doesn’t run any commands that require elevation, and you’re pretty sure you won’t have to worry about anyone’s custom profiles getting in the way, you can skip the rest of this. If you are running some Administrator-level cmdlets though, you’ll need this piece.
如果您的脚本没有运行任何需要提升的命令,并且您可以确定不必担心任何人的自定义配置文件会妨碍您,则可以跳过其余部分。 但是,如果您正在运行某些管理员级别的cmdlet,则需要此代码。
Unfortunately, there’s no way to trigger UAC for elevation from within a batch file or CMD session. However, PowerShell does allow us to do this with Start-Process. When used with “-Verb RunAs” in its arguments, Start-Process will try to launch an application with Administrator permissions. If the PowerShell session isn’t already elevated, this will trigger a UAC prompt. To use this from the batch file for launching our script, we’ll end up spawning two PowerShell processes – one to fire off Start-Process and another, launched by Start-Process, to run the script. The second line of the batch file needs to be changed to this:
不幸的是,无法从批处理文件或CMD会话中触发UAC进行海拔提升。 但是,PowerShell确实允许我们使用“启动过程”来执行此操作。 在其参数中与“ -Verb RunAs”一起使用时,Start-Process将尝试启动具有管理员权限的应用程序。 如果尚未提升PowerShell会话,则将触发UAC提示。 为了从批处理文件中使用该脚本来启动脚本,我们将最终生成两个PowerShell进程-一个用于启动Start-Process,另一个由Start-Process启动,以运行脚本。 批处理文件的第二行需要更改为:
PowerShell.exe -Command "& {Start-Process PowerShell.exe -ArgumentList '-ExecutionPolicy Bypass -File ""%~dpn0.ps1""' -Verb RunAs}"
PowerShell.exe -Command "& {Start-Process PowerShell.exe -ArgumentList '-ExecutionPolicy Bypass -File ""%~dpn0.ps1""' -Verb RunAs}"
When the batch file is run, the first line of output we’ll see is from the PowerShell profile script. Then, there will be a UAC prompt when Start-Process tries to launch MyScript.ps1.
运行批处理文件时,我们将看到的第一行输出来自PowerShell配置文件脚本。 然后,当Start-Process尝试启动MyScript.ps1时,将出现一个UAC提示。
After clicking through the UAC prompt, a new PowerShell instance will spawn. Because this is a new instance, of course, we’ll again see the profile script notice. Then, MyScript.ps1 runs and we see that we are indeed in an elevated session.
单击UAC提示符后,将生成一个新的PowerShell实例。 因为这是一个新实例,所以我们将再次看到配置文件脚本通知。 然后,MyScript.ps1运行,我们看到我们确实处于提升的会话中。
And there’s the reason we have two pauses in here, too. If not for the one in the PowerShell script, we’d never see the script’s output – the PowerShell window would just pop up and disappear as soon as the script is done running. And without the pause in the batch file, we wouldn’t be able to see if there were any errors launching PowerShell in the first place.
这也是我们在这里也有两个停顿的原因。 如果不是PowerShell脚本中的脚本,我们将永远看不到脚本的输出–一旦脚本运行完成,PowerShell窗口就会弹出并消失。 而且如果没有在批处理文件中暂停,我们将无法首先查看启动PowerShell时是否有任何错误。
步骤4:解决自定义PowerShell配置文件。 (Step 4: Getting around custom PowerShell profiles.)
Let’s get rid of that nasty custom profile notice now, shall we? Here, it’s hardly even a nuisance, but if a user’s PowerShell profile changes default settings, variables, or functions in ways you may not have anticipated with your script, they can be really troublesome. It’s much simpler to run your script without the profile entirely so you don’t have to worry about this. To do that, we just need to change the second line of the batch file one more time:
现在让我们摆脱那讨厌的自定义配置文件通知,对吧? 在这里,这几乎没有什么麻烦,但是如果用户的PowerShell配置文件以您脚本可能无法预期的方式更改默认设置,变量或函数,则它们确实很麻烦。 完全不使用概要文件来运行脚本要简单得多,因此您不必为此担心。 为此,我们只需要再次更改批处理文件的第二行:
PowerShell.exe -NoProfile -Command "& {Start-Process PowerShell.exe -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File ""%~dpn0.ps1""' -Verb RunAs}"
Adding the -NoProfile parameter to both instances of PowerShell that are launched by the script means that the user’s profile script will be completely bypassed in both steps and our PowerShell script will run in a fairly predictable, default environment. Here, you can see there’s no custom profile notice in either of the spawned shells.
将-NoProfile参数添加到脚本启动的两个PowerShell实例中,意味着用户的配置文件脚本将在两个步骤中完全绕开,并且我们的PowerShell脚本将在相当可预测的默认环境中运行。 在这里,您可以看到两个衍生外壳中都没有自定义配置文件通知。
If you don’t need Administrator rights in your PowerShell script, and you’ve skipped Step 3, you can do without the second PowerShell instance and the second line of your batch file should look like this:
如果您在PowerShell脚本中不需要管理员权限,并且已跳过步骤3,则可以不用第二个PowerShell实例,而批处理文件的第二行应如下所示:
PowerShell.exe -NoProfile -ExecutionPolicy Bypass -Command "& '%~dpn0.ps1'"
PowerShell.exe -NoProfile -ExecutionPolicy Bypass -Command "& '%~dpn0.ps1'"
The output will then look like this:
输出将如下所示:
(Of course, for non-Administrator scripts, you could do without an end-of-script pause in your PowerShell script at this point too since everything is captured in the same console window and would be held there by the pause at the end of the batch file anyway.)
(当然,对于非管理员脚本,此时您也可以在PowerShell脚本中没有脚本结尾暂停,因为所有内容都捕获在同一个控制台窗口中,并在结束时通过暂停保留在该控制台窗口中。批处理文件。)
完成的批处理文件。 (Completed batch files.)
Depending on whether or not you need Administrator permissions for your PowerShell script (and you really shouldn’t be requesting them if you don’t) the final batch file should look like one of the two below.
根据您是否需要PowerShell脚本的管理员权限(如果您确实不需要,则不应该请求它们),最终的批处理文件应类似于以下两个文件之一。
Without Admin access:
没有管理员访问权限:
@ECHO OFF PowerShell.exe -NoProfile -ExecutionPolicy Bypass -Command "& '%~dpn0.ps1'" PAUSE
With Admin access:
具有管理员访问权限:
@ECHO OFF PowerShell.exe -NoProfile -Command "& {Start-Process PowerShell.exe -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File ""%~dpn0.ps1""' -Verb RunAs}" PAUSE
@ECHO OFF PowerShell.exe -NoProfile -Command "& {Start-Process PowerShell.exe -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File ""%~dpn0.ps1""' -Verb RunAs}" PAUSE
Remember to put the batch file in the same folder as the PowerShell script you want to use it for, and give it the same name. Then, no matter what system you take those files to, you’ll be able to run your PowerShell script without having to muck around with any of the security settings on the system. You could certainly do those changes manually every time, but this saves you that trouble and you won’t have to worry about reverting the changes later.
请记住,将批处理文件与要用于其的PowerShell脚本放在同一文件夹中,并为其命名。 然后,无论将这些文件放入哪个系统,都可以运行PowerShell脚本,而无需考虑系统上的任何安全设置。 当然,您每次都可以手动进行更改,但这可以为您省去麻烦,并且您不必担心以后还原更改。
References:
参考文献:
-
Running PowerShell scripts from a batch file – Daniel Schroeder’s Programming Blog
从批处理文件运行PowerShell脚本– Daniel Schroeder的编程博客
-
Checking for Administrator permissions in PowerShell – Hey, Scripting Guy! Blog
-
› How to Quickly Scroll Through Home Screen Pages on iPhone and iPad
-
› How to Move a Window to Another Virtual Desktop on Windows 10
-
› What’s the Deal with Google Home and Nest? Is There a Difference?
翻译自: https://www.howtogeek.com/204088/how-to-use-a-batch-file-to-make-powershell-scripts-easier-to-run/