第21章 你把这叫做脚本
21.2 使得命令可重复执行
PowerShell脚本背后的理念,首先是使得重复执行特定命令变得简单,而无须每次手动重复输入命令。
此时,我们需要开始使用PowerShell ISE而不是标准的控制台窗口。这是由于通过ISE将我们的命令转为一个脚本变得更加容易。坦白讲,由于可以使用全屏的编辑器而不是在控制台宿主上输入单行命令,ISE使得输入复杂命令变得更加容易。
下面是我们的命令。
Get-WmiObject -class Win32_LogicalDisk -computer localhost -filter "drivetype=3" |
Sort-Object -property DeviceID |
Format-Table -property DeviceID,
@{label='FreeSpace(MB)';expression={$_.FreeSpace / 1MB -as [int]}},
@{label='Size(GB)';expression={$_.Size / 1GB -as [int]}},
@{label='%Free';expression={$_.FreeSpace / $_.Size * 100 -as [int]}}
我们可以通过单击在工具栏的绿色运行按钮(也可以按快捷键F5)运行命令,对命令进行测试,输出结果显示命令正常工作。你也可以选中命令的一部分并按F8键,从而只运行选中部分的命令。
21.3 参数化命令
我们暂时将计算机名称变量设置为静态值。下面是修改后的脚本。
$computername = 'localhost'
Get-WmiObject -class Win32_LogicalDisk `
-computer localhost `
-filter "drivetype=3" |
Sort-Object -property DeviceID |
Format-Table -property DeviceID,
@{label='FreeSpace(MB)';expression={$_.FreeSpace / 1MB -as [int]}},
@{label='Size(GB)';expression={$_.Size / 1GB -as [int]}},
@{label='%Free';expression={$_.FreeSpace / $_.Size * 100 -as [int]}}
21.4 创建一个带参数的脚本
既然我们已经识别出了脚本中每次执行可能变化的部分,那么我们就需要提供一种让其他人赋予这些元素新值的方式。换句话说,我们需要将被赋予常量的$computername变量转变为一个输入参数。
PowerShell中创建一个带参数的脚本非常简单。
param (
$computername = 'localhost'
)
Get-WmiObject -class Win32_LogicalDisk -computer localhost `
-filter "drivetype=3" |
Sort-Object -property DeviceID |
Format-Table -property DeviceID,
@{label='FreeSpace(MB)';expression={$_.FreeSpace / 1MB -as [int]}},
@{label='Size(GB)';expression={$_.Size / 1GB -as [int]}},
@{label='%Free';expression={$_.FreeSpace / $_.Size * 100 -as [int]}}
我们只需要在变量声明代码附近添加一个param()块。这会将$c
omputerName定义为一个参数,并在未对参数赋值时指定localhost作为默认值。
所有以这种方式定义的参数是命名参数,也是位置参数。
你可以通过逗号作为分隔符指定任意数量的参数。例如,假如我们还希望将过滤条件设置为参数。当前脚本仅获取类型为3的驱动器,也就是硬盘。我们可以将值变为参数。如代码清单21.3所示。
代码清单21.3 Get-DiskInventory.ps1,包含一个额外参数
param (
$computername = 'localhost',
$drivetype = 3
)
Get-WmiObject -class Win32_LogicalDisk -computer $computername `
-filter "drivetype=$drivetype" |
Sort-Object -property DeviceID |
Format-Table -property DeviceID,
@{label='FreeSpace(MB)';expression={$_.FreeSpace / 1MB -as [int]}},
@{label='Size(GB)';expression={$_.Size / 1GB -as [int]}},
@{label='%Free';expression={$_.FreeSpace / $_.Size * 100 -as [int]}}
我们可以以最开始的3种方式运行该脚本。当然,我们也可以通过忽略参数的方式使用参数的默认值。下面是一些该脚本的使用示例。
PS C:\>.\Get-DiskInventory.ps1 server-r2 3
PS C:\>.\Get-DiskInventory.ps1 -comp server-r2 -drive 3
PS C:\>.\Get-DiskInventory.ps1 server-r2
PS C:\>.\Get-DiskInventory.ps1 -drive 3
21.5 为脚本添加文档
只有真正吝啬的人才会创建一个有用的脚本,而不告诉任何人如何使用它。幸运的是,PowerShell提供了简单的方式为脚本添加帮助,也就是通过注释。你当然可以为你的脚本添加典型编程风格的注释,但如果你已经在脚本中使用了完整的Cmdlet名称和参数名称,很多时候你的脚本的意图已经可以望文生义。通过使用特殊的注释语法,你可以提供模仿PowerShell本身帮助文档的帮助信息。
代码清单21.4展示了我们为脚本添加的内容。
代码清单21.4 为Get-DiskInventory.ps1添加帮助
<#
.SYNOPSIS
Get-DiskInventory retrieves logical disk information from one or more computers.
.DESCRIPTION
Get-DiskInventory uses WMI to retrieve the Win32_LogicalDisk instance from one or more computers.It displays each disk's drvie letter,free space,total size,and percentage fo free space.
.PARAMETER computername
The computer name,or names to query.Default:Localhost.
.EXAMPLE
Get-DiskInventory -computername SERVER-R2 -drivetype 3
#>
param (
$computername = 'localhost',
$drivetype = 3
)
Get-WmiObject -class Win32_LogicalDisk -computer $computername `
-filter "drivetype=$drivetype" |
Sort-Object -property DeviceID |
Format-Table -property DeviceID,
@{label='FreeSpace(MB)';expression={$_.FreeSpace / 1MB -as [int]}},
@{label='Size(GB)';expression={$_.Size / 1GB -as [int]}},
@{label='%Free';expression={$_.FreeSpace / $_.Size * 100 -as [int]}}
正常情况下,PowerShell都会忽略以#开头的代码行,意味着#用于标识某一行是注释。而我们使用<# #>块注释语法,这是由于我人需要注释多行,而不希望在第一行开始都使用#。
现在我们可以使用标准的控制台宿主,并通过运行Help .\Get-Inventory命令获取帮助。(再一次,我们需要提供路径,这是由于该脚本并不是一个内置Cmdlet。)
21.6 一个脚本,一个管道
当你分别运行命令时,你会为每个命令创建一个新的管道。在每一个管道末尾,PowerShell会查看哪一列需要被格式化并创建一个你可以看到的表格。这里的重点是“不同命令运行在不同管道中”。
在PowerShell中,所有的命令都在一个管道中执行,在脚本中也是同样。在脚本中,任何产生管道输出结果的命令都会被写入同一个管道中:脚本自身运行的管道。
两种不同的输出是由于将两种类别的对象放入同一个管道中。这是将命令存入脚本和手动执行之间的重要区别:在脚本中,只能够使用一个管道。正常来讲,你的脚本应该努力保持只输出一类对象,以便PowerShell能产生合理的文本输出格式。
21.7 作用域初探
作用域是特定类型PowerShell元素的容器,这些元素主要是别名、变量和函数。
Shell本身具有最高级别的作用域,称为全局域(global scope)。当运行一个脚本时,会在脚本范围内创建一个新的作用域,也就是所谓的脚本作用域(script scope)。脚本作用域是全局作用域的子集,也就是全局作用域的子作用域(child)。而全局作用域是脚本作用域的父作用域(parent)。函数还有其特有的私有作用域(private scope)。
作用域的生命周期只持续到作用域所需执行的最后一行代码之前。主要规则是,如果你尝试访问一个作用域元素,PowerShell在当前作用域内查找;如果不存在于当前作用域,PowerShell会查找其父作用域,依此类推,直到找到树形关系的顶端——也就是全局作用域。