欢迎光临 Enjoy IT (ITECN.NET) 登录 | 注册 | 帮助

PowerShell V2 介绍(9)– 事务机制

对数据库有所了解的朋友肯定听说过事务日志这个术语,事务日志的一个作用是保证数据写入的完整性。在Windows PowerShell V2中也引入了事务机制,要实现事务机制,cmdlet和PSProvider都必须支持事务机制。下面就让我们来看看哪些cmdlet和PSProvider支持事务机制。
首先我们使用这条命令“Get-Help * -Parameter UseTransaction”来查看支持事务机制的cmdlet:
image
以上截图只是所有支持事务机制的cmdlet的一部分,支持事务机制的cmdlet共有37条,大家可以通过运行刚才的命令来查看详细的命令列表。接下来让我们来看下支持事务机制的PSProvider一共有多少。我们可以使用这个命令来查看:
get-psprovider | where {$_.Capabilities -like "*transactions*"}image
强烈的反差是不是让你感到很不适应,但是事实就是截止到目前为止,只有注册表支持事务机制,不过这并不妨碍我们学习事务机制。
最后,让我们来看下和事务机制相关的cmdlet有哪些。还是使用老方法来确认:Get-Command *transaction*
image
至此,我们大体知道了事务日志涉及到PowerShell中哪些方面的内容。下面让我们来看看如何使用事务日志。
接下来将假设这样一个情境。我们编写了一个PowerShell脚本,该脚本会利用注册表来存储信息,供脚本每次运行时使用,下面的代码就是一个这样的示例脚本(仅包含写入和读取注册表信息的函数):

Function Write-Config
{
if (!(Test-Path hkcu:\SoftWare\MyApp1))
{
New-Item hkcu:\SoftWare\MyApp1 | Out-Null
}
$colItems = Get-ChildItem Env:
foreach ($objItem in $colItems)
{
New-ItemProperty hkcu:\SoftWare\MyApp1 -Name $objItem.Name -Value $objItem.Value | Out-Null
}
Write-Host "Done!"
}

Function Read-Config
{
$objItem = Get-ItemProperty -Path hkcu:\SoftWare\MyApp1 -Name windir
Write-Host "The item value is: "$objItem.windir
}

Function Start-App
{
if ((Test-Path hkcu:\SoftWare\MyApp1))
{
Read-Config
}
else
{
Write-Config
}
}

Start-App

这里一共定义了三个函数,一个用来写入数据,一个用来读取数据,一个用来在读取配置信息后来执行正真的代码。我们使用环境变量中的相关值来充当脚本所需的一些配置信息。在写入数据的时候很可能出现这样的情况,用户因为某些理由使用Ctrl+C强制终止脚本的执行,这时脚本并没有往注册表内写入完整的信息,比如windir的值就没有被写入注册表。当然,有的朋友或许会说PowerShell的执行效率不是很高么,在用户按Ctrl+C的时候,所有信息都被写入注册表了。的确,这种情况是有可能发生,所以大家在测试脚本的时候可以在循环语句中插入Start-Sleep来降低数据写入的速度,这样能更容易的模拟问题的发生情境。因为事务机制考虑的是在最坏的条件如何使得数据能被完整地读取和写入。
在说完脚本的情况后,我们再来接着说说问题的发生情境。刚才已经提到了,在数据写入的时候,用户可能按Ctrl+C终止脚本的执行,导致写入的信息不完整。这时候如果我们在编写脚本时没有考虑到这种情况,那么在用户下次运行脚本时就会导致问题的发生。比如我们的脚本在启动时只判断hkcu:\SoftWare\MyApp1路径是否存在,如果存在就认为脚本在上次启动时已经成功保存了配置,但实际上我们现在都知道了,这种判断依据很显然存在重大的漏洞。那么我们就没有简单的办法来规避这个问题的发生么?

在Windows PowerShell V2中我们可以利用事务机制轻松的解决这个问题。不过再修改刚才的那个示例脚本之前,我们再来看一个简单的例子来了解下如何使用事务机制。

Start-Transaction
New-Item hkcu:\Software\MyApp2 -UseTransaction
Write-Host "Without UseTransAction parameter"
Get-Item hkcu:\Software\MyApp2
Write-Host "With UseTransAction parameter"
Get-Item hkcu:\Software\MyApp2 -UseTransaction
Complete-Transaction

首先,我们必须使用Start-Transaction cmdlet来启动事务机制(行1)。接着我们使用支持事务机制的cmdlet来写入信息,写入的时候必须加上-UseTransaction参数,告诉PowerShell该命令将使用事务机制(行2)。此时虽然命令已经成功执行,大家可能认为数据已经写入注册表中,其实不然,如果我们此时使用Get-Item cmdlet来查看数据有没有被写入的时候将有错误发生(行4)。当然我们也不是没有办法来查看刚才启用事务日志后写入的信息,只是我们同样在使用Get-Item cmdlet时需要加上-UseTransaction参数(行6)。最后我们使用Complete-Transaction来结束事务机制并写入所有相关信息(行7)。当然在最后如果你想要撤销利用事务日志写入的信息的话,可以使用Undo-Transaction cmdlet来替代Complete-Transaction。

在明白了事务机制的使用流程之后,我们就可以着手改造之前的示例脚本了。经过刚才的介绍,相信大家心里也知道如何修改了,下面我们来看看修改完成后的脚本:

Function Write-Config
{
Start-Transaction #-Timeout 5 -RollbackPreference Never
if (!(Test-Path hkcu:\SoftWare\MyApp1))
{
New-Item hkcu:\SoftWare\MyApp1 -UseTransaction | Out-Null
}
$colItems = Get-ChildItem Env:
foreach ($objItem in $colItems)
{
New-ItemProperty hkcu:\SoftWare\MyApp1 -Name $objItem.Name -Value $objItem.Value -UseTransaction | Out-Null
#Write-Progress -Activity "Transaction Test" -CurrentOperation $objItem.Name -Status "Writing Config..."
#Start-Sleep -Milliseconds 500
}
Complete-Transaction
Write-Host "Done!"
}

Function Read-Config
{
$objItem = Get-ItemProperty -Path hkcu:\SoftWare\MyApp1 -Name windir
Write-Host "Value is: "$objItem.windir
}

Function Start-App
{
if ((Test-Path hkcu:\SoftWare\MyApp1))
{
Read-Config
}
else
{
Write-Config
}
}

Start-App

脚本中变化最大的部分自然是Write-Config这个函数,在使用事务机制以后,我们能保证所有配置信息被写入注册表。被注释掉的代码的作用就是用来模拟故障发生的。
首先Start-Transaction cmdlet 的Timeout参数的作用是设置事务机制的超时时间,参数值的类型是整数,代表分钟数,这里我设置的值是5。而RollbackPreference参数是用来控制事务机制的自动回滚,这里我设的值是Never,表示禁止事务机制自动回滚。这两个参数的作用是避免后面脚本中使用Start-Sleep后出现的错误。

image

其次是后面的Write-Progress和Start-Sleep代码。这两句代码很好理解,Write-Progress提供直观的写入进度,让我们从容决定什么时候按Ctrl+C来模拟故障发生。而Start-Sleep则是用来控制配置信息的写入速度。
最后如果大家要模拟故障的话,可以把注释符号去掉,然后在脚本执行过程中按Ctrl+C终止脚本执行。终止脚本执行后,注册表内不会写入任何配置信息。该脚本仅当Complete-Transaction执行完成后才会在注册表内写入配置信息,这样就保证了配置信息能完整写入,同时也降低了我们后期读取配置信息时的代码编写难度。

本次关于如何使用Windows PowerShell V2 事务机制的介绍就到此结束了,敬请期待下次的介绍。

发表于 作者 ghjconan | 0 评论

PowerShell V2 介绍(8)- 模块

通过之前三篇关于Windows PowerShell v2高级函数的介绍,相信大家对于如何在PowerShell中整合相关代码都有了一定了解。在平时的工作中,相信大家都会把常用的代码整理在一起,以备不时之需。在VBScript时代,如果我们需要在新脚本中重复利用这些代码,基本上就是把它们复制到新的脚本中。而在Windows PowerShell v2中引入了模块的概念,我们可以直接将保存在文件中的相关代码通过特定的cmdlet导入到当前的PowerShell会话中,下面就让我们来看看如何使用Windows PowerShell v2的模块功能。

首先我们还是使用老方法“Get-Command *module* -CommandType cmdlet”来看看和模块相关的cmdlet有哪些。命令的运行结果如下:

image

大家可以看到命令不是很多,所以使用起来不是很复杂。我们除了使用Windows 7中自带的模块外,也能使用自己创建的模块。在本次介绍中我们主要来看看如何使用自己创建的模块。接下来我们就来看看如何创建模块。

下面我将利用WMI的Win32_UserAccount类提供的信息和功能来编写一个简单的用于演示的本地帐号管理模块。这个模块涉及到两个文件,一个是后缀名为.psm1的模块文件,里面包含三个函数。还有一个是后缀名为.psd1的Manifest文件。下面先来看看后缀名为.psm1中的文件有哪些内容。

$nameSpace = "root\cimv2"
$wmiClass = "Win32_UserAccount"

Function Get-UsersList
{
[CmdletBinding(SupportsShouldProcess=$True,ConfirmImpact="none")]
Param
(
[Parameter(Mandatory=$false,Position=0)]
[boolean]$ShowProperties
)
Process
{
$colUsers = Get-WMIobject -Namespace $nameSpace -Class $wmiClass `
-Filter "LocalAccount='True'"
if ($ShowProperties)
{
$colUsers | Format-List Name,SID,Lockout,Disabled
}
else
{
$colUsers | Format-Table Name,SID -AutoSize
}
}
}

Function Set-User
{
[CmdletBinding(SupportsShouldProcess=$True,ConfirmImpact="High")]
Param
(
[Parameter(Mandatory=$true,Position=0)]
[string]$Name,
[Parameter(Mandatory=$false,Position=1)]
[boolean]$Disabled
)
Process
{
$objUser = Get-WMIobject -Namespace $nameSpace -Class $wmiClass `
-Filter "Name='$Name' And LocalAccount='True'"
if ($Disabled)
{
$objUser.Disabled=$true
$objUser.Put() | Out-Null
}
else
{
$objUser.Disabled=$false
$objUser.Put() | Out-Null
}
}
}

Function Get-LocalAdminName
{
$objUser = Get-WMIobject -Namespace root\cimv2 -class Win32_UserAccount `
-Filter "LocalAccount='True' And SID LIKE '%-500'"
Write-Host "Local Administrator Name:"$objUser.Name
}

在示例模块中,我利用WMI的Win32_UserAccount类一共编写了三个函数,分别是Get-UsersList,Set-User以及Get-LocalAdminName。作用分别是显示用户列表,禁用和启用帐号以及得到本地管理员的名称。其中Get-UsersList和Set-User是高级函数。Get-UsersList的 ShowProperties参数用来控制显示的信息。Set-User的Disabled的参数用来控制是否禁用或者启用账号。相信这些函数的代码大家理解起来困难都不大,因为主要工作都是有Get-WMIObject cmdlet来完成的,这个cmdlet大家应该很熟悉了。最后我们将以上代码保存为lusrmgr.psm1 。

作为示例用的函数虽然很简单,但是一个模块就是由这样的函数累积而成,大家也可以试试扩展下这个模块。接下来我们来看看如何生成后缀名是.psd1的Manifest文件。

首先,Manifest文件的作是用让模块编写者可以提供关于模块的相关信息,比如编写者,编写者所在的公司,关于模块作用的描述等内容。虽然我们可以用Import-Module直接导入后缀名为.psm1的文件,但是很多模块可能不单单只包含一个psm1的文件,有可能还包含dll文件,比如Windows 7中的BitsTransfer模块,这时我们就需要使用Manifest文件了。接下来我们将使用New-ModuleManifest cmdlet来创建该文件。

用于New-ModuleManifest cmdlet参数比较多,而且有些参数是必要参数,大家可以使用“help New-ModuleManifest –full”来查看所有帮助信息。下面我给出的是包含所有必要参数的命令。

New-ModuleManifest ` 
-Author "ghjconan" `
-CompanyName "ITECN" `
-CopyRight "(c) 2009 ghjconan" `
-Description "Local User Account Management" `
-FileList "lusrmgr.psm1" `
-FormatsToProcess @() `
-ModuletoProcess "lusrmgr.psm1" `
-NestedModules @() `
-Path "C:\PowerShell\Modules\Lusrmgr\lusrmgr.psd1" `
-RequiredAssemblies @() `
-TypesToProcess @()

这里需要注意的是ModuletoProcess参数的值是我们的模块文件的名称,通常我们会把psm1文件和Manifest文件放在一起,所以这里直接用的相对路径。而FileList参数的值则是我们要包含在模块中文件集合。而Path参数则是最终Manifest文件的输出路径。最后在执行命令后将生成包含以下内容的文件。

#
# 模块“lusrmgr”的模块清单
#
# 生成者: ghjconan
#
# 生成时间: 2009/6/14
#

@{

# Script module or binary module file associated with this manifest
ModuleToProcess = 'lusrmgr.psm1'

# Version number of this module.
ModuleVersion = '1.0'

# ID used to uniquely identify this module
GUID = '9f6229d5-9bbc-40ee-86dc-9fffcbd6550a'

# Author of this module
Author = 'ghjconan'

# Company or vendor of this module
CompanyName = 'ITECN'

# Copyright statement for this module
Copyright = '(c) 2009 ghjconan'

# Description of the functionality provided by this module
Description = 'Local User Account Management'

# Minimum version of the Windows PowerShell engine required by this module
PowerShellVersion = ''

# Name of the Windows PowerShell host required by this module
PowerShellHostName = ''

# Minimum version of the Windows PowerShell host required by this module
PowerShellHostVersion = ''

# Minimum version of the .NET Framework required by this module
DotNetFrameworkVersion = ''

# Minimum version of the common language runtime (CLR) required by this module
CLRVersion = ''

# Processor architecture (None, X86, Amd64, IA64) required by this module
ProcessorArchitecture = ''

# Modules that must be imported into the global environment prior to importing this module
RequiredModules = @()

# Assemblies that must be loaded prior to importing this module
RequiredAssemblies = @()

# Script files (.ps1) that are run in the caller's environment prior to importing this module
ScriptsToProcess = @()

# Type files (.ps1xml) to be loaded when importing this module
TypesToProcess = @()

# Format files (.ps1xml) to be loaded when importing this module
FormatsToProcess = @()

# Modules to import as nested modules of the module specified in ModuleToProcess
NestedModules = 'lusrmgr.psm1'

# Functions to export from this module
FunctionsToExport = '*'

# Cmdlets to export from this module
CmdletsToExport = '*'

# Variables to export from this module
VariablesToExport = '*'

# Aliases to export from this module
AliasesToExport = '*'

# List of all modules packaged with this module
ModuleList = @()

# List of all files packaged with this module
FileList = 'lusrmgr.psm1'

# Private data to pass to the module specified in ModuleToProcess
PrivateData = ''

}

大家可以看到Manifest文件中的很多属性其实是和基于PowerShell的二次开发有很大关系的,刚才我们的命令只是创建了最基础的属性,为后续的模块导入做准备。

至此,我们已经拥有了lusrmgr.psm1和lusrmgr.psd1这两个文件,我们可以把他们放在名为lusrmgr的文件夹里。如果大家不想复制和粘贴上面的代码的话可以下载文后的附件。

接下来我们来看看如何使用模块。使用模块共分四步:

1. 安装模块
2. 导入模块至PowerShell会话
3. 查找导入的命令
4. 使用导入的命令

1. 安装模块

虽然我们可以从任意文件夹中安装模块,但是为了方便管理,建议大家还是新建一个文件夹来集中放置我们从各种途径得到的模块。我们可以使用new-item来完成新建文件夹的任务,然后使用copy-item将得到模块复制到新建的文件夹中。在PowerShell v2正式发布之后,相信会出现越来越多的模块,到时大家可能得到简单的包含模块的压缩包文件,或者是一个独立的安装文件。无论是安装还是解压缩,我们都需要将模块放置到指定的文件夹,这一过程和我们安装程序类似,因此称之为安装模块。

PowerShell的默认的系统模块文件夹位于$pshome\modules文件夹下,用户模块文件夹则位于%userprofile%\Documents\WindowsPowerShell\Modules或者%userprofile%\My Documents\WindowsPowerShell\Modules。我们可以通过修改环境变量来更改默认模块文件夹的位置,当然为了实现永久修改,我们是需要修改PowerShell的配置文件或者直接修改系统环境变量里的PSModulePath。这个看大家需要。

因此大家可以把附件中的lusrmgr文件夹移动到你创建的专门用来放置模块的文件夹,在我的环境中,我使用的文件夹路径是 C:\PowerShell\Modules\。

2. 导入模块至PowerShell会话

接下来我们就可以使用Import-Module来导入模块了。针对lusrmgr而言,Import-Module使用过程很简单。我们可以在Import-Module后直接更上lusrmgr文件夹的路径,同时我们可以使用-Verbose参数来查看导入过程中具体执行了哪些动作:

image

3. 查找导入的命令

刚才我们在打开Verbose参数以后,在控制台窗口中已经显示了lusrmgr模块中具体包含了哪些命令。但是如果我们使用cls清屏之后自然就看不到这些信息了,所以还需要另外的方式来查看模块中的命令。当然这个命令大家其实很熟悉,就是我们常用的Get-Command。为了查看模块中的命令,我们需要使用Get-Command的Module参数,具体命令如下

Get-Command –Module lusrmgr

image

这样,无论我们从何处得到模块,我们都可以通过Get-Command命令来查看模块编写者为我们准备的命令。

4. 使用导入的命令

是否能简单的使用导入的命令,主要看模块编写者提供的方法使用起来是否简单。在之前高级函数的介绍中,我们已经提到了通过使用PowerShell V2中的高级函数可以达到类似cmdlet的操作体验。因此,lusrmgr模块中的命令使用起来并不复杂。下面就来看一下之前提到的几个命令

Get-UsersList及Get-UsersList –ShowProperties

image

Set-User

image

Get-LocalAdminName

image

本次关于如何使用Windows PowerShell v2中模块功能的介绍就到此结束了,敬请期待下一次的介绍。

 

发表于 作者 ghjconan | 0 评论
Attachment(s): lusrmgr.zip

PowerShell V2 介绍(7)- 高级函数(下)

今天我们将来介绍高级函数的最后一部分内容。共分两个方面,一是如何使用注释内容作为高级函数的帮助信息,而是如何使用高级函数的动态参数功能。接下来我们先来看一下高级函数如何利用注释内容作为其帮助信息,以及捎带介绍下如何使用参数集。这部分内容,我还是给出一段示例代码,代码中不会执行任何实际的功能,只是单纯的测试我提到的两点内容。而有关更多如何使用基于注释内容的帮助,大家可以通过输入“help about_Comment_Based_Help -full”查看,主要是掌握几个关键词的应用。

Function Test-AdvancedFunction
{
    <#
        .SYNOPSIS
        这是一个测试函数用来说明高级函数如何将注释内容转化为自身的帮助信息
        以及如何指定参数属于哪个参数集
        .Description
        这是一个测试函数用来说明高级函数如何将注释内容转化为自身的帮助信息
        以及如何指定参数属于哪个参数集
        要了解更多内容请输入"help about_Comment_Based_Help -full"查看
        .Link
        How to Write Cmdlet Help
http://msdn.microsoft.com/en-us/library/aa965353.aspx
    #>

    [CmdletBinding(SupportsShouldProcess=$True,
    ConfirmImpact="none",
    DefaultParameterSetName="FileSystem")]
    Param
    (
        [parameter(Mandatory=$true,
        Position=0,
        ParameterSetName="FileSystem")]
        [int]$Param1,
        [parameter(Mandatory=$true,
        Position=1,
        ParameterSetName="FileSystem")]
        [int]$Param2,
        [parameter(Mandatory=$true,
        Position=0,
        ParameterSetName="Registry")]
        [int]$Param3,
        [parameter(Mandatory=$true,
        Position=1,
        ParameterSetName="Registry")]
        [int]$Param4
    )
}

接下来还是让我们来看一下最终效果,而查看效果的命令很简单,就是我们常用的“help Test-AdvancedFunction -full”

image

首先大家可以看下摘要信息。这是我在注释信息中指定的说明信息。然后我们接着看到语法这一块。很明显Param1、Param2与Param3、Param4分别对应于不同的使用方法,这就是DefaultParameterSetName及ParameterSetName联合作用下产生的结果。在高级函数中嵌入帮助信息有助于他人了解我们所编写的高级函数的作用,与在代码中嵌入注释信息相比,这种方式更直观也更接近我们平常的使用体验。

接下来我们将介绍如何使用高级函数的动态参数功能。下面先给出相关代码:

Function Test-DynamicParam
{
[CmdletBinding(SupportsShouldProcess=$True,ConfirmImpact="High")]
Param
(
[Parameter(Mandatory=$True,Position=0)]
[String]$Param1,
[Parameter(Mandatory=$True,Position=1)]
[String]$Param2
)
DynamicParam
{
if ($Param1 -match "powershell")
{

$paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary

$dyParam3 = New-Object `
System.Management.Automation.RuntimeDefinedParameter("Param3", `
[String],$attrCol1)

$attrCol1 = New-Object System.Management.Automation.ParameterAttribute
$attrCol1.Mandatory = $True
$dyParam3.Attributes.Add($attrCol1)

$paramDictionary.Add("Param3",$dyParam3)

return $paramDictionary
}
}
process
{
Write-Host "Param1: $Param1`nParam2: $Param2"
if ($dyParam3 -ne $null)
{
Write-Host "Param3:"$dyParam3.Value
}
}
}

接着,我们来具体看一下相关代码的具体含义。

首先我们按照上次介绍中的方法使用CmdletBinding将此函数定义为高级函数,然后我们定义了两个静态参数,分别为Param1和Param2.接着是本次介绍的主角,动态参数(DynamicParam)。和Begin,Process,End类似,DynamicParam代表是代码块。在DynamicParam中,我们通常使用if语句对已知参数值进行判断,如果符合特定条件的话,我们就创建动态参数。这里我使用的判断条件是Param1的值中是否存在PowerShell,如果有PowerShell就创建动态参数Param3。接下来让我们来看一下动态参数的创建过程。

动态参数的创建将使用.Net的相关类。首先我们初始了System.Management.Automation.RuntimeDefinedParameterDictionary的一个实例$dyParam3(这里的对象名称切勿与参数名相同)。该类代表了在运行时创建的参数集合,DynamicParam返回的对象必须是RuntimeDefinedParameterDictionary,否则PowerShell将报错。接下来我们将使用System.Management.Automation.RuntimeDefinedParameter来定义具体的参数。在构造该类的实例时,我们可以依次指定我们所需的参数名,参数类型,以及参数的属性集。这里分别是参数名Param3,参数类型[string],以及等下我们要定义的参数属性集$attrCol1。

下面我们就要定义的是$attrCol1。这与我们在上次的介绍中提到的如何在Param中定义参数的属性有点类似,只不过我们这次将使用原生的.Net方法。首先使用New-Object初始ParameterAttribute类的一个实例$attrCol1。接着我们为该实例的Mandatory属性赋值,当这个属性的值为真时表明该参数是必要参数。最后,我们将属性集添加到之前我们初始的参数对象$dyParam3中。

接下来,我们将该参数添加到参数集$paramDictionary中,然后使用Return退出DynamicParam代码块。

接下来我们在Process中使用Write-Host来显示我们输入的参数值。需要注意的是我使用了if语句,使得Param3参数存在时才输出该参数值。

最后,就是测试环节了。我们可以用以下代码来测试:

Write-Host "Test1" 
Test-DynamicParam "powershell" "dynamicparam"
Write-Host "`n"
Write-Host "Test2"
Test-DynamicParam "dynamicparam" "powershell"

测试结果如下:

image

我们总共进行了两次测试来完成对比。第一次我们将Param1参数值设为PowerShell,按照预先的定义,此时PowerShell将会生成动态参数。如图所示,在参数属性Mandatory的作用下,PowerShell提示用户为Param3提供参数值。最后的输出结果也符合我们的预期。

第二次测试时Param1参数值不为PowerShell,此时不应该生成动态参数。最终测试结果于我们的预期也相符。

本次关于如何使用动态参数的介绍就到此结束了,同时关于高级函数的介绍也行将告一段落。希望各位除了看我的介绍文章之外能多看看帮助文档来尽快熟悉高级函数的使用方法。

下次我们将介绍如何使用Windows PowerShell v2的模块功能,敬请期待。

 

发表于 作者 ghjconan | 0 评论

PowerShell V2 介绍(6)- 高级函数(中)

在上次的介绍中我们主要介绍了Windows PowerShell V2 中高级函数的一些基础概念,在本次介绍中我们将要涉及的是如何使用高级函数达到类似cmdlet的使用体验(下面就以高级函数来称呼)。

首先还是让我们来看一下高级函数是如何构成的:

Function Test-AdvancedFunction 
{
[CmdletBinding(SupportsShouldProcess=<Boolean>,
ConfirmImpact=<String>,
DefaultParameterSetName=<String>)]
Param ($Parameter1)
Begin{}
Process{}
End{}
}

大家一定注意到了最开始的CmdletBinding,接下来让我们来详细看下这个属性及它自身的一些成员属性(相关的帮助信息可以通过“help about_Functions_CmdletBindingAttribute”来查看)。

  • SupportsShouldProcess
    可选参数值为$True, $False。当设置为$True时,高级函数将支持通用参数-whatif以及-confirm。
  • ConfirmImpact
    可选参数值为High, Low, Medium, None。关于这个参数我们会在稍后通过代码实例说明。
  • DefaultParameterSetName
    代表默认的参数集名称。参数集就是我们在使用帮助命令时看到的语法内容,这部分内容将放在下次的介绍中进行说明。

为了与DynamicParam所定义的参数区别开来,我们姑且称有Param定义的参数为静态参数。接下来让我们来看下如何定义静态的参数。静态参数的定义本身并不复杂,复杂的地方在于如何选择参数的属性来达到自己想要的效果。下面给出相关参数属性的列表:

属性名

可选参数值

属性说明

Mandatory

$True, $False

指定参数是否是必要参数,强制用户输入

Position

整数

指定参数位置,如果用户没有指定具体参数名称,那么PowerShell将根据该值按序填充相应的参数

ParameterSetName

字符串

指定该参数属于哪个特定的参数集

ValueFromPipeline

$True, $False

是否接受来自管道中的值

ValueFromPipelineByPropertyName

$True, $False

是否接受来自管道中指定参数名的值

ValueFromRemainingArguments

$True, $False

是否接受来自管道中的剩余参数

HelpMessage

字符串

描述参数作用的帮助信息

Alias

字符串

指定参数的另一个名称

下面的参数是用来对用户输入的参数进行验证

AllowNull

允许对象为空

AllowEmptyString

允许字符串为空

AllowEmptyCollection

允许集合为空

ValidateCount

整数

检验可以接受的参数个数

ValidateLength

整数

检验参数的长度

ValidatePattern

正则表达式

使用正则表达式来检验参数

ValidateRange

整数范围

检验参数值是否在指定范围内

ValidateScript

表达式

使用代码来检验参数值

ValidateSet

集合

检验参数值是否在指定的属性集合中

ValidateNotNull

检验参数是否为非空对象

ValidateNotNullOrEmpty

检验参数是否为非空字符串

这里虽然给出了完整的可用参数属性列表,但并不是说我们每设置一个参数都要指定这么多属性,我们可以按需使用相应的属性。如果各位要查看如何使用具体每一个属性,可以参考这里
接下来我将给出一段模拟计算器的示例代码,我们结合代码来看如何结合参数属性定义静态参数。

Function Test-Calc
{
[CmdletBinding(SupportsShouldProcess=$True,ConfirmImpact="none")]
Param
(
[ValidateRange(1,100)]
[parameter(Mandatory=$true,
Position=0,
HelpMessage="请输入数字1")]
[int]$Number1,
[parameter(Mandatory=$true,
Position=1,
HelpMessage="操作符")]
[ValidateSet("+","-","*","/","Max","Min")]
[string]$Operator,
[ValidateRange(1,100)]
[parameter(Mandatory=$true,
Position=2,
HelpMessage="请输入数字2")]
[int]$Number2
)
process
{
switch ($Operator)
{
"+" {$result = $Number1 + $Number2}
"-" {$result = $Number1 - $Number2}
"*" {$result = $Number1 * $Number2}
"/" {$result = $Number1 / $Number2}
"Min" {$result = [Math]::Min($Number1,$Number2)}
"Max" {$result = [Math]::Max($Number1,$Number2)}
}
}
end
{
Write-Host "Result : $Number1 $Operator $Number2 = $result"
}
}

首先Param后面使用的是小括号(),而不是花括号{},这点大家需要注意。其次我们有可能需要定义很多个参数,同时也会指定很多参数的属性,此时在注意合理换行的同时,也要注意括号匹配的问题。我们来看下这两个参数的定义:

[ValidateRange(1,100)]
[parameter(Mandatory=$true,
Position=0,
HelpMessage="请输入数字1")]
[int]$Number1,
[parameter(Mandatory=$true,
Position=1,
HelpMessage="操作符")]
[ValidateSet("+","-","*","/","Max","Min")]
[string]$Operator,

在最开始,我们对使用了用户参数进行验证的ValidateRange,它可以独立成行,并且使用中括号[]来定义。接着我们看到了Parameter,所有的参数属性都必须包含在Parameter后的()中,之间使用,分隔。最后我们定义参数的具体类型,同时我们需要注意不同参数之间也是用,进行分隔的,因此这里的,和不同括号可能非常让人头晕眼花,在定义的时候经常出错,所以大家定义的时候还需仔细。当然参数属性的部分,也可以写成这样:

[ValidateRange(1,100)]
[parameter(Mandatory=$true,Position=0,HelpMessage="请输入数字1")]
[int]$Number1,
[parameter(Mandatory=$true,Position=1,HelpMessage="操作符")]
[ValidateSet("+","-","*","/","Max","Min")]
[string]$Operator,

这样做的缺点也很明显,如果使用的参数属性较多或者参数属性值过长,会造成一行代码过长。因此这两种方法,大家可以视具体情况综合使用。

接下来就让我们来看看上面代码的测试效果。

1. Test-Calc 1 Max a(参数类型验证,参数位置验证)

image

2. Test-Calc(强制用户输入参数,显示帮助信息)

image

3. Test-Calc 10000 max 1(参数值范围验证)

image

以上内容就是如何使用Param在高级函数中定义静态参数,大家可以结合参数属性列表多练习下就能掌握了。接下来我们来看下开始说到的ConfirmImpact。

在PowerShell中,当用户执行的操作涉及到PowerShell自身之外的系统环境时,PowerShell将根据cmdlet中的相关设置来请求用户是否要继续操作。这里我们可以通过观察Stop-Process cmdlet来了解一二。首先,我们可以打开一个记事本程序,然后使用“Stop-Process notepad”来终止进程。然后大家再尝试下使用“Stop-Process winlogon”来终止winlogon进程。很明显当我们执行第二个命令的时候,PowerShell发出确认请求,询问用户是否要终止“winlogon”进程:

clip_image002[4]
(这部分测试请在虚拟机下进行,如果没有任何提示确认是否要结束winlogon进程,而导致用户强制被注销进而使得您未保存的文件内容丢失的话,本人不负任何责任)

在PowerShell V1中,为了达到类似的效果,Jeffrey为我们写了一个专门的函数,当时PowerShell Team也承诺将在PowerShell V2中原生支持这个功能,而不需要额外编写函数来实现。这就是ConfirmImpact的由来。下面就让我们来看看如何使用。

Function Test-Output
{
[CmdletBinding(SupportsShouldProcess=$True,
ConfirmImpact="High")]
Param
(
[Parameter(Mandatory=$True,
Position=0)]
[string]$Path
)
Process
{
if($path -like "*system*")
{
if ($PSCmdlet.ShouldProcess($path))
{
Out-File -FilePath $path
}
}
}
}

我们可以看到ConfirmImpact的参数值被设置为High,表示该高级函数所执行的操作比较危险,需要谨慎操作。(当然示例中只是演示了往不同的路径写入一个文件)。

以上代码的作用是在指定路径下写入一个文件,但是我们在高级函数中将写入路径中包含System的定义为危险操作。需要在用户确认才执行写入操作,因此我们用到了$PSCmdlet对象的ShouldProcess方法。该方法的作用是显示提示信息,返回值是一个布尔值,因此我们使用if语句来进行判断、最终该方法能在PowerShell提示符中生成以下效果的选项,让用户进行选择。

接下来我们可以受用下面两条命令来测试ConfirmImpact设置为High时高级函数的执行效果:

Test-Output -Path c:\PowerShell\a.txt
Test-Output -Path c:\windows\System32\a.txt

image 

然后是ConfirmImpact设置为none时的测试结果

image.png" width="639" height="79" />

大家可以看到完全没有出现任何提示信息。当然细心的朋友可能会发ConfirmImpact还可以设置两个参数值Low和Medium,这两个值在什么时候发挥作用呢?这里我们需要介绍下自动变量$ConfirmPreference。在PowerShell会话中,高级函数在进行确认操作时默认会和自动变量$ConfirmPreference进行比较,如果发现高级函数中设置的ConfirmImpact等级高于$ConfirmPreference时就会发出提示,比如下面例子中高级函数中ConfirmImpact被设置为Medium,而$ConfirmPreference设置为Low,那么在高级函数执行危险操作时就会发出提示。

image

如果$ConfirmPreference的值低于ConfirmImpact的值(比如None< Low),那么,PowerShell就不会请求用户是否执行操作了。

本次介绍的内容比起上次的介绍可能一下子深入了很多,可能有些朋友理解起来有些难度。但是千万不要因此而放弃,高级函数是PowerShell中很重要的一块内容,多写写相关代码就会熟悉起来的。

本次的介绍就到此结束了,下次我们将介绍在高级函数中如何利用注释信息作为高级函数的帮助提供给Get-Help cmdlet使用以及如何使用动态参数,敬请期待!

发表于 作者 ghjconan | 0 评论

PowerShell V2 介绍(5)- 高级函数(上)

Windows PowerShell V2是一款正在开发中的产品,文中介绍的功能与将来的正式版本中提供的功能相冲突时,请以正式版本为准。

从本次介绍开始,我使用的是Windows 7中的PowerShell。各位在测试Windows 7的同时不妨来看看PowerShell。Windows 7中PowerShell所带的帮助文件和CTP3相比已经有所更新了,所以推荐大家使用Windows 7中的PowerShell。

熟悉脚本编写的朋友对函数(Function)这个名词一定不会陌生,函数是一系列语句集合而成的代码块,我们可以为其命名。然后只需调用函数名称便能执行函数中的代码,这种方式使得我们无需每次都输入一大段代码来完成一些特定的功能。

在Windows PowerShell v2中,函数有了很大的增强。高级函数在执行操作时可以得到与cmdlet类似的操作体验。毕竟对广大使用PowerShell的非开发人员而言,与使用.NET语言去编写cmdlet相比,使用熟悉的PowerShell语法去编写函数还是很简单的。今天我们主要先熟悉下高级函数的一些概念,在下次的介绍中我们再来谈谈如何使用高级函数来实现与cmdlet类似的操作体验。

可能大家觉得编写函数还不简单么,随手就能写一个出来:

Function Hello($userName)
{
Write-Host "Hello $userName!"
}
Hello Anna


即使是功能复杂的函数也大同小异,没什么复杂的。但实际上我们如果去仔细阅读帮助的话,会发现完整的函数是有以下形式构成的:

function [<scope:>]<name> [([type]$parameter1[,[type]$parameter2])]  
{
param([type]$parameter1 [,[type]$parameter2])
dynamicparam {<statement list>}
begin {<statement list>}
process {<statement list>}
end {<statement list>}
}

下面让我们来仔细看看上面的结构。函数名没什么好说的,但在函数名之前的<scope:>要说一下,同变量一样,函数也是有作用域的,比较常用的作用域有Global和Script。Global作用于整个PowerShell会话,只要PowerShell会话不结束,被Global修饰的变量和函数都是可用的。而Script仅作用于脚本执行期间,一旦脚本执行完毕,脚本中被Script修饰的变量和函数都不在可用。以上内容,我们可以用下面的代码进行演示:

$Script:userName1 = "Anna"
$Global:userName2 = "Bob"
Function Script:Script_Hello($userName)
{
Write-Host "Hello $userName!"
}
Function Global:Global_Hello($userName)
{
Write-Host "Hello $userName!"
}
Script_Hello $userName1
Script_Hello $userName2

演示效果如下:

image

接下来我们需要注意的是函数参数的放置位置,我们既可以使用和VBScript中函数类似的参数放置位置,也可以将参数放置在代码块中,只不过此时需要使用param。接着还需注意的是在PowerShell中还可以指定参数类型,我们接着用刚才的代码进行演示:

Function Script:Add1([int]$num1,[int]$num2)
{
$total = $num1 + $num2
Write-Host $total
}
Function Script:Add2
{
param([int]$num1,[int]$num2)
$total = $num1 + $num2
Write-Host $total
}

add1 1 2
add2 1 2
add1 $number1 a


演示效果如下:

image

这里需要注意的是参数类型,如果PowerShell没有办法隐式的将参数值转换成相应的参数类型的话就会发生上面的错误。示例中的字符串显然没办法转换成整数。

接下来我们会看到dynamicparam {<statement list>},dynamicparam 是Windows PowerShell v2中新增的关键字,在关于高级函数的最后一次介绍中,我们再来介绍它。下面我们继续往下看。

接下来我们看到的是Begin、Process和End三个关键字。我们知道函数是可以接收来自管道的输入数据的,而我们可以通过Begin、Process和End来控制函数如何去处理来自管道的输入数据。首先,Begin代表的代码块在函数开始执行时执行一次,并且在整个函数执行过程中只执行一次,此时的函数还没有接收到来自管道的输入数据。接下来是Process关键字。Process代表的代码块将针对每一个输入对象运行一次,当Process代码块正在处理数据时,每个对象都被赋值给$_变量。当函数接收完来自管道中的数据对象后,End代表的代码块将执行一次。如果我们没有在函数中指定Begin、Process和End关键字,那么函数中的所有语句将被PowerShell默认为End代码块中的语句。

这里我们还不得不提一下两个变量$_和$input。$_代表的是当前管道中的对象,而$input则代表来自管道的所有对象,并且这个变量仅在函数和脚本块中使用。这两个变量将影响到我们在函数中如何处理数据。说了那么多,大家可能觉得头很晕,下面还是让我们来结合代码来看下这部分的介绍。具体代码如下:

Function Test-Function 
{
begin
{
Write-Host "[Begin] : $input"
}
process
{
Write-Host "[Process] : $_"
}
end
{
Write-Host "[End] : $input"
}
}
1,2,3 | Test-Function

这部分代码很简单,我们将通过管道向函数传递三个数字,然后通过Write-Host来输出处理结果,通过输出的内容来考察Begin、Process和End是如何处理数据的。下面是示例代码的运行效果:

.png">image

大家首先看到在Begin代码执行后,PowerShell仅输出了提示信息“[Begin]:”,这是正常现象,因为此时函数还没有接收到来自管道的输入数据,因此$input变量是空的。接下来我们看到输出了三行数据,这说明Process中的代码块对每一个来自管道的对象进行了处理。接下我们会发现最后End代码块中的Write-Host没有输出$input变量的值,这是因此如果在我们的函数中存在Process代表的代码块的话,$input在Process处理完之后将被清空。如果我们将示例代码中的Process代码块注释掉,然后再执行示例代码的话,大家会看到以下结果:

image

本次主要向大家介绍了高级函数的一些基础知识点,可能很多朋友都比较熟悉了。关于高级函数的内容将分三次介绍,下次我们将介绍如何使用高级函数来实现类似cmdlet的操作体验,尽请期待。

发表于 作者 ghjconan | 0 评论

PowerShell V2 CTP3介绍(4)- 简述后台任务(下)

Windows PowerShell V2是一款正在开发中的产品,文中介绍的功能与将来的正式版本中提供的功能相冲突时,请以正式版本为准。

相信各位通过上次的介绍,已经大致了解了后台任务在本地计算机上的执行步骤。今天要介绍的是在远程计算机上执行后台任务。

在介绍具体使用方式之前,必须对实验环境做一下配置, 允许WinRM使用CredSSP验证。这是因为在PSSession中使用Start-Job cmdlet时必须使用CredSSP验证。具体配置过程如下:

1. 在DC上的Windows PowerShell v2窗口中执行以下命令:

Enable-WSManCredSSP -DelegateComputer client

clip_image001

2.在Client上的Windows PowerShell v2窗口中执行以下命令(也可以使用组策略配置):

cd WSMan:\localhost\Service\Auth\

Set-Item CredSSP -Value True

clip_image002

说完必要的配置后,再来看看具体的使用方式。在Windows PowerShell v2中在远程计算机上执行后台任务时共有三种方式,分别是:

  • 与远程计算机建立PSSession,并在PSSession中使用后台任务。这种方式与在本地计算机上使用后台任务相差无几。
  • 在远程计算机上运行后台任务,并将结果返回给本地计算机。这种方式通常用于收集远程计算机的相关信息。
  • 在远程计算机上运行后台任务,并将结果保留在远程计算机。这种方式增强后台任务数据的安全性。

下面分别说说这三种在远程计算机上执行后台任务的方法。

与远程计算机建立PSSession,并在PSSession中使用后台任务

在上次介绍的最后,我希望各位能熟悉执行后台任务的整个流程。一旦你熟悉了在本地计算机上执行后台任务的整个流程,那么只要结合本系列介绍的前两篇提到的远程管理功能,你就掌握了一种在远程计算机上使用后台任务的方法。因此在今天的介绍中我不会花大篇幅来介绍这个方法,大家只需知道这种方法比在本地使用后台任务只是多了建立PSSession的步骤。因此这里就不在多做介绍了,只需注意在使用Enter-PSSession时将验证方式改为CredSSP,并指定相关用户凭据。具体过程如下图所示:

clip_image003

在远程计算机上运行后台任务,并将结果返回给本地计算机

在上次介绍的开篇,我提到了可以使用两条命令来得到和后台任务有关的所有cmdlets。在上篇介绍中主要介绍了Get-Command *job所得到的cmdlets,本次将介绍get-help * -parameter asjob得到的cmdlets。

当然,限于篇幅不可能将每个命令都讲一遍。这次着重介绍的是Invoke-Command。

先来说说Invoke-Command。Invoke-Command是Windows PowerShell V2中新引入的cmdlets之一,正如字面意义所示,它的作用就是在本地和远程计算机上执行命令。比如以下命令:

Invoke-Command -ScriptBlock {hostname} -ComputerName client

命令结果如下:

clip_image004

这使得我们在执行远程管理操作时除了利用PSSession之外又多了一种方式。

话题再说回后台任务。正如大家在上次截图中所看到,Invoke-Command cmdlet支持asjob参数,也就是说它支持后台任务。具体步骤如下:

1. 使用Invoke-Command及asjob参数在远程计算机上启动后台任务,具体命令如下:

Invoke-Command -ScriptBlock {Get-EventLog -Logname "Windows PowerShell" -Newest 5} -ComputerName client -AsJob

2. 使用Get-Job查看后台任务是否执行完成,具体命令如下:

Get-Job

3. 当Get-Job cmdlet显示相关后台任务状态为以完成时,利用Receive-Job来得到后台任务的执行结果。具体命令如下(其中的ID号视具体环境而定):

Receive-Job -Id 3

最终整个过程如下图所示:

clip_image005

比起建立PSSession的方法,利用Invoke-Command是非常快捷的。这里还不得不提一下Invoke-Command的Computer参数。该参数支持输入多台计算机名,计算机名之间用 , 分隔。这意味着我们可以同时在多台计算机上启动后台任务。接下里就可以小憩一会,等待后台任务执行完成,然后查看执行结果。这个例子的执行过程如下:

clip_image006

在远程计算机上运行后台任务,并将结果保留在远程计算机

最后再来说说在远程计算机上运行后台任务,并将结果保留在远程计算机。这种方法一看就知道结果保留在远程计算机,而不经过网络传递到本地计算机无疑增加了数据安全性。当然有朋友可能看到这里会感兴趣,上面用Invoke-Command执行的后台任务,在网络上传输后台任务的执行结果时,难道是没有加密的么?当然是加密的,以图为证:

clip_image007

当然作为一种在远程计算机上运行后台任务的方法,我们还是须要大致了解整个执行过程的,以备不时之需,具体步骤如下:

1. 使用New-PSSession建立永久性会话,然后使用Invoke-Command运行后台任务。这是因为使用Invoke-Command的Computer参数所建立的会话是临时性的,当后台任务任务执行完成后,临时会话也会关闭,最终将得不到后台任务执行的结果。具体命令如下:

$session = New-PSSession -ComputerName client -Credential $cred -Authentication CredSSP

invoke-command -session $session -scriptblock {start-job -scriptblock {hostname}}

2.使用Invoke-Command得到远程计算机上后台任务的使用状态

invoke-command -session $session -scriptblock {Get-Job}

3. 当后台任务执行完成后使用Receive-Job cmdlet得到运行结果,并将结果导出为文本文件保存在远程计算机上,具体命令如下:

invoke-command -session $session -scriptblock {Receive-Job -Id 1 > c:\posh\results.txt}

这个例子的执行过程如下图所示:

clip_image008

至此为止,在远程计算机运行后台任务的三种方法全部介绍完了,不知道诸位觉得哪种方法简单点。我个人是认为第二种比较简单,不需要去调整身份验证方法,输入的命令也比较简洁。当然根据最终环境的不同,我们是需要使用不同的方法。

最后再次强调一下在PSSession中使用Start-Job必须将身份验证方式设为CredSSP,否者会收到以下错误:

Connecting to remote server failed with the following error message : 拒绝访问。

clip_image009

好了,今天的内容就是这么多了,敬请期待下一次的PowerShell V2 CTP3介绍。

发表于 作者 ghjconan | 0 评论

PowerShell V2 CTP3介绍(3)- 简述后台任务(上)

Windows PowerShell V2是一款正在开发中的产品,文中介绍的功能与将来的正式版本中提供的功能相冲突时,请以正式版本为准。

在Windows PowerShell V2中引入了后台任务的概念。后台任务可以使得cmdlets在后台异步运行而不用与控制台交互。我们可以在方便的时候查询后台任务运行的结果,后台任务可以运行于本地或远程计算机。下面我们使用两个命令来看看和后台任务相关的cmdlets有哪些。

首先使用的命令是:Get-Command *job。该命令将告诉我们以下cmdlets和后台任务相关:

clip_image001

然后使用的命令是:get-help * -parameter asjob。该命令将告诉我们以下cmdlets的参数与后台任务有关:

clip_image002

通过以上两个命令我们就知道了后台任务涉及到了哪些cmdlets。在开始的介绍中,大家已经知道后台任务既可以在本地运行,也能在远程计算机上运行。本次介绍主要涉及在本地计算机运行后台任务,在下次的介绍中我们再来看看如何在远程计算机上执行后台任务。

在本地计算机上测试后台任务

通过执行Get-Command *job命令我们已经知道,后台任务一共涉及了六个cmdlets。下面我将按照示例脚本中的顺序来给各位介绍后台任务。所用命令包含在以下脚本中:

Start-Job -ScriptBlock {Get-EventLog -LogName "Windows PowerShell" -Newest 1}
Get-Job | Wait-Job
Get-Job | Receive-Job | Select-Object EventID,EntryType,TimeWritten
Get-Job | Remove-Job

启动后台任务

后台任务可以通过Start-Job cmdlet启动。在上面的脚本中启动了一个读取PowerShell日志的后台任务。通过观察,很容易了解Start-Job cmdlet的使用方法。我们只需将需要后台执行的命令包含在花括号中,并将包含花括号在内的整个字符串作为参数ScriptBlock的参数值即可。

接下来我们再来看看脚本的第二行。首先是Get-Job cmdlet。单看cmdlet名称可能让人觉得是用来得到后台任务执行结果的,其实不然。Get-Job cmdlet的作用是得到当前会话中的所有后台任务对象,而不是后台任务的执行结果。然后再来看看Wait-Job cmdlet。Wait-Job cmdlet的作用是在当前会话中所有后台任务执行完成之前挂起Windows PowerShell,阻止用户输入命令。虽然这和后台任务在后台执行,用户可以继续在前台输入这个理念相背,但是有些时候我们还是需要这样的功能的,因此Wait-Job cmdlet还是很有意义的。

获得后台任务的执行结果

刚才我已经说了Get-Job cmdlet的作用仅仅是得到当前会话中的后台任务对象,而真正用来获得后台任务执行结果的是Receive-Job cmdlet。也就是脚本第三行代码的作用。而Select-Object的作用是得到指定属性,在示例中的作用仅是简化输出内容。

删除后台任务

当后台任务执行完成后,如果我们不希望保留相关后台任务对象的话可以使用Remove-Job cmdlet来删除后台任务对象。也就是示例脚本中最后一行代码的作用。

最终整个示例脚本的执行结果如下图所示:

clip_image003

当然细心的朋友看到这里肯定会发现我没有介绍Stop-Job。接下来就说说Stop-Job cmdlet的作用。还是以Get-Eventlog cmdlet为例进行说明。在刚才的示例脚本中,大家可以看到在使用Get-Eventlog cmdlet时,我将Newest参数的值设置为1,这样做是为了命令能尽快执行完成。如果不指定该参数,并且以后台任务的形式执行Get-Eventlog cmdlet的话,那么后台任务将持续一段时间,而这段时间内我们因为某种原因想要停止执行后台任务时,便需要使用Stop-Job cmdlet。比如说原来打算通过后台任务获得系统日志的,结果输入的命令的是获得应用程序日志的,此时正是Stop-Job的用武之地,让错误的后台任务的停止下来,不用继续做无用功。

通过以上介绍,不难发现在本地使用后台任务还是很容易的。本周的内容差不多到此就结束了,希望大家能熟悉下使用后台任务的整个流程,下次介绍在远程计算机上执行后台任务时,也是需要理解这整个流程的。

敬请期待下次的"PowerShell V2 CTP3介绍(4)- 简述后台任务(下)"

发表于 作者 ghjconan | 0 评论

PowerShell V2 CTP3介绍(2):PSSession

Windows PowerShell V2是一款正在开发中的产品,文中介绍的功能与将来的正式版本中提供的功能相冲突时,请以正式版本为准。

在上篇文章中我通过Get-HotFix这个简单的例子简略地介绍了PowerShell v2中的远程管理功能。本周的介绍中我将详细介绍下PowerShell v2中的远程管理功能。在上周的介绍中引入了Enter-PSSession和Exit-PSSession这两个cmdlet,那还有没有其它包含PSSession的cmdlet呢?答案可以通过执行以下命令获得:
Get-Commands *PSSession

Enter-PSSession Exit-PSSession
Import-PSSession Export-PSSession
New-PSSession Remove-PSSession
Get-PSSession  

在介绍以上cmdlets之前,先让我们了解下PSSession的一些概念。

Session和PSSession

首先Session是Windows PowerShell的运行环境,每次启动Windows PowerShell时都会创建一个初始的Session,你可以在这个Session中执行相关操作。在Windows PowerShell v2中,你能创建,控制和管理额外的Session。这些Session称为"Windows PowerShell Session"或者"PSSession"。对远程管理而言,这个特性是很关键的。基于这个特性,你能同时打开多个PSSession与远程计算机建立连接,当管理任务完成之后便可以将特定的PSSession结束掉,而不影响其它Session。

在这里举一个大家比较熟悉的例子。在IE7中引入了多页面功能,在IE启动的时会默认打开一个页面,我们可以把这个页面理解成启动PowerShell时所创建的初始Session,要想关闭这个页面(初始Session),就必须关闭IE(Windows PowerShell)本身。但如同我们可以IE7中打开多个页面浏览网页一样,在PowerShell V2中我们也能利用PSSessions来连接多台计算机。

我们可以使用New-PSSession和Remove-PSSession连建立和结束PSSession。

PSSession的存活时间

当我们用 New-PSSession -Computer Client 建立PSSession时,默认情况下这个PSSession将一直存在直到你关闭Windows PowerShell窗口或者使用Remove-PSSession结束,也就是说这个会话将"永久"存在。如果觉得手动结束PSSession很麻烦的话,那么可以在使用New-PSSession时指定Timeout参数的值(单位为秒)。这也是另一个影响PSSessions存活时间的因素。不过该参数在PowerShell v2 CTP3中还没有实现,我们需要耐心等待。而我们使用Enter-PSSession建立起的是一个临时性的交互式对话,当运行完所有需要执行的命令后,使用Exit-PSSession将结束这个临时Session而不是退出。因此请注意区分Exit-PSSession在不同类型的PSSession(永久性的和临时性的)中执行的效果。

其它注意事项

要使用Windows PowerShell v2的远程管理功能,你必须以管理员身份运行PowerShell
当前用户必须是远程计算机本地管理员组中的成员或者能在建立连接时提供适当凭据
每次只有一个PSSession处于激活状态,但其它PSSession将会保留在后台

说了这么多概念,相信各位也厌烦了,下面我们将通过一个实验来介绍下cmdlets。实验环境还是上次用到的那个简单的域环境。

1 . 以管理员身份运行Windows PowerShell v2,然后执行Get-PSSession,没有任何结果返回,这表明现在我们还没有建立任何PSSession。

clip_image001

2. 利用New-PSSession建立两个PSSession,由于实验环境中我只有两台计算机,因此我将一个PSSession连接到本地。并且将命令返回的对象保存在变量中供后续使用。

clip_image002

3. 再次执行Get-PSSession,查看当前已经建立的PSSession。

clip_image003

4. 接下来我们使用Enter-PSSession进入相应的PSSession执行命令。并在命令执行完成后退出相应的PSSession。注意Enter-PSSession可使用的参数,我们既能使用ID参数来输入通过Get-PSSession获得PSSession的ID值,也能通过将之前保存的相应变量赋值给Session参数。这里我将使用后一种方式。

clip_image004

5. 再次运行Get-PSSession,查看是否还有PSSession存在。在这里不妨回想下刚才我提到的PSSession的生存时间。

clip_image005

6. 接下来在我们结束会话之前,再来看看Import-PSSession和Export-PSSession这组cmdlets。Import-PSSession的作用是将一个会话中的cmdlets, functions, aliases, 及其它类型的命令导入到当前会话中。我们接着使用刚才建立的那两个会话来测试Import-PSSession(注意我将使用参数来选择仅导入指定的function)。大致的过程就是在$pss1代表的会话中建立一个function来获得主机名,然后通过Import-PSSession导入到当前会话,然后再在当前会话中执行这个原本不存在的function。具体过程效果如下图。 顶部的出错信息就是由于当前会话中没有Get-Hostname这个function报的错。

clip_image006

而Export-PSSession的功能与Import-PSSession有点类似,区别在于Export-PSSession将结果导出到Windows PowerShell 脚本模块(.psm1)文件中而不是当前会话。如果我们需要将结果导入到当前会话中则需要再运行Add-Module cmdlet。不过在PowerShell v2 CTP3中该cmdlet的Export-PSSession filepath参数还没有实现,因此还不能给大家演示这个功能。

7. 最后我们需要结束当前存在的两个PSSession,通过使用管道,我们能很轻松的关闭所有打开的PSSession。

clip_image007

通过这次介绍,想必各位对PowerShell v2 CTP3中的远程管理功能有了进一步的了解。想要详细了解远程管理功能的话,请多多阅读PowerShell自带的帮助文档,当然要注意文档中的提到的某些功能在CTP3还未实现。

那么,敬请各位期待下一次的Windows PowerShell v2 CTP3 新功能介绍。

发表于 作者 ghjconan | 0 评论

PowerShell V2 CTP3介绍(1): 浅谈远程管理功能

前言

不知道各位春节休息的怎么样,但是无论我们多么怀念春节休假时每天睡到自然醒的美好时光,新一年的工作又要开始了。对广大的ITPro而言今年又有很多东西要学习。虽然Windows PowerShell V2目前还没有正式发布,并且还处于CTP3阶段,但是在Beta版中的Windows 7和Windows Server 2008 R2中已经包含了此版本的Windows PowerShell。并且和以往在Vista和Windows Server 2008 中不同的是,PowerShell V2在最新版本的Windows中已经默认安装了。而我们都知道Windows 7和Windows Server 2008 R2离正式发布也不是那么的遥不可及了。因此从现在开始了解Windows PowerShell V2将有助于我们用崭新的管理方式去管理新一代的操作系统。

接下来的一个问题是,怎么样学习Windows PowerShell V2呢?对Exchange 2007比较熟悉的朋友或多或少应该听说过Exchange 2007的帮助文档被称为一件艺术品,因为文档内容非常详实。而Windows PowerShell V2在帮助文档比起V1版本也有了很大进步。现在我们可以通过查阅chm文档了解Windows PowerShell V2了,而不需要在PowerShell下输入Get-Help来查阅一个个零散的帮助信息了。

在Windows Server 2008下 PowerShell 帮助文档WindowsPowerShellHelp.chm位于 C:\Windows\Help\mui\0409\ 下,各位可以将它复制到易于访问的位置,供随时学习和查阅。

在正式介绍Windows PowerShell v2之前,还是要强调一句:
Windows PowerShell V2是一款正在开发中的产品,文中介绍的功能与将来的正式版本中提供的功能相冲突时,请以正式版本为准。

准备工作

在本次的PowerShell v2 CTP3介绍中,我将使用由两台虚拟机组成的简单实验环境。由于硬件条件限制,我将使用两台操作系统为Windows Server 2008的虚拟机,这两台虚拟机将组成一个最简单的域环境,域名为contoso.com。充当域控制器的虚拟机计算机名为DC,IP地址为192.168.100.1。而客户端的计算机名为Client,IP地址为192.168.100.2。域的搭建过程在此就不在赘述了。

因为Windows Server 2008中自带的是V1版的Windows PowerShell,所以我们需要下载PowerShell V2(下载),而为了测试PowerShell V2中的远程管理功能,我们还需要下载WinRM 2.0(Connect站点下载 ),同时为了使用PowerShell v2所带的集成脚本编写环境(Integrated Scripting Environment,ISE),你还必须安装.Net Framework 3.5.1(可以通过Windows Server 2008服务器中的”添加功能“安装)。

使用组策略启用WinRM

等以上所需软件在两台计算机上都安装完成后,我们继续通过组策略启用PowerShell远程功能的依赖项:WinRM。具体步骤如下:

1. 在域控制器上,运行组策略管理控制台。定位到【计算机配置】-【策略】-【Windows 设置】-【安全设置】-【系统服务】。在右侧列表中找到”Windows Remote Management(WS-Management)“,并设置服务启动模式为”自动“。

clip_image001

2. 定位到【计算机配置】-【策略】-【管理模板】-【Windows 组件】-【WinRM Services】,修改“Allows automatic configuration of listeners”为“已启用”,并将“IPv4 Filters”的值修改为“*”。

clip_image002

3. 定位到【计算机配置】-【策略】-【管理模板】-【网络】-【网络连接】-【Windows 防火墙】-【域配置文件】,修改“定义入站端口例外”。添加80端口。注意,此时的防火墙配置中我们也无需开启以往WMI使用的DCOM端口。

clip_image003

4. 在客户端刷新组策略,并测试WinRM是否正常工作。

由于在客户端上我是以普通域用户的身份登录操作系统的,所以需要以管理员身份启动命令提示符。然后在提升为管理员权限的命令提示符下输入以下命令:winrm enumerate winrm/config/listener,结果如下图:

clip_image004

上图说明我们已成功通过组策略启用WinRM。

接着我们在域控制器端运行以下命令来检测能否通过WinRM来获得客户端计算机打印服务的相关信息。

winrm get wmicimv2/Win32_Service?name=spooler -machine:client.contoso.com

clip_image005

测试Windows PowerShell V2远程管理功能

在完成上述配置后,我们就可以对PowerShell v2的远程管理功能进行测试了。

先从Windows PowerShell V1中已经存在的功能说起。Windows PowerShell V1中的Get-WMIObject cmdlet有一个computer参数,我们可以使用该cmdlet来获得远程计算机的相关信息。而在Windows PowerShell V1中也仅有该cmdlet带有computer这个参数,这一点可以通过运行”get-help * -parameter ComputerName“确认。而在PowerShell v2如果我们运行这个命令会发现支持computer参数的cmdlet增加了不少,下面就是相关cmdlets的列表。

Get-WinEvent 

Get-Counter  

Invoke-Command

New-PSSession

Get-PSSession

Remove-PSSession

Receive-Job

Enter-PSSession

Get-EventLog

Clear-EventLog

Write-EventLog

Limit-EventLog

Show-EventLog

New-EventLog

Remove-EventLog

Get-WmiObject

Invoke-WmiMethod

Get-Process

Remove-WmiObject 

Get-Service

Set-Service

Set-WmiInstance

Get-HotFix

Restart-Computer

Stop-Computer

Add-Computer

Remove-Computer

Rename-Computer

Reset-ComputerMachinePassword

 

不少朋友有时会比较关心高管使用的电脑有没有打上最新补丁,那么相信这些朋友会很快发现上述列表中有一个非常有用的cmdlet:Get-HotFix。

clip_image006

不过由于这个cmdlet没有利用Windows PowerShell V2中新的远程管理体系,而是使用了原来的WMI,因此如果你没有打开相应端口的话,该命令将返回错误信息:

clip_image007

为了解决这个问题,在Windows PowerShell V2中使用了新的远程管理体系。而新的远程管理体系基于WinRM服务,也就是在本文一开始我们所配置的服务。我们可以使用Enter-PSSession来启动一个与远程计算机间的交互式会话。下面还是使用Get-HotFix来说明。

首先我们使用以下命令与客户端计算机建立起会话:

Enter-PSSession client

几秒之后,如果一切正常,那么PowerShell中的相关提示将会告诉我们已经连接到了远程计算机:

clip_image008

而此时如果我们再运行Get-Hotfix cmdlet的话,就会得到我们想要的信息了:

clip_image009

最后,我们可以通过运行Exit-PSSession来结束会话。

今天我们简单测试了PowerShell v2中的远程管理功能,在下次的介绍中,将详细介绍PSSession以及和PSSession有关的cmdlets。

发表于 作者 ghjconan | 0 评论

PowerGUI 介绍之脚本编辑器

    这次我们要介绍的是PowerGUI中自带的脚本编辑器。PowerGUI脚本编辑器是一个带丰富调试功能的集成开发环境。

    clip_image001

    首先要介绍的是PowerGUI脚本编辑器集成开发环境的几个特性:

  1. 语法高亮

    经常写脚本的朋友对语法高亮这个词肯定很熟悉。该特性能帮助我们在编写脚本过程中避免一些低级的拼写错误。在PowerGUI脚本编辑器中支持语法高亮的有 cmdlets,别名,语句标识符(ifforforeach等等),变量,.NET 类,注释,引号中的字符串。

    clip_image002

  2. 智能感知

    同样这是对经常使用脚本编写工具的朋友而言非常熟悉的一个词。该特性帮助我们在编写脚本时能迅速找到需要使用的对象和功能。在PowerGUI脚本编辑器中支持智能感知的有cmdlets,参数,WMI对象,.NET对象,变量。

    clip_image003

    clip_image004

     

  3. 代码段

    通过这个功能,我们能够重复利用一些特定的功能。该特性对熟悉VBScript中相关函数,但是在使用PowerShell时却找不到相关函数的朋友非常有用。比如在VBScript中格式化百分数时有一个非常有用的函数叫FormatPercent,而在PowerShell中对字符串进行格式化是稍微有点麻烦的,但是我们通过代码段这个功能可以方便的知道如何在PowerShell中格式化百分数:

    clip_image005

  4. PowerGUI脚本编辑器中获得帮助信息

    PowerGUI脚本编辑器为我们提供了更友好的帮助信息的呈现方式。当鼠标指向cmdlets时,编辑器将提供一个浮动提示,来简单说明cmdlet的功能和相关的参数。

    clip_image006

    Windows下的很多软件在提供帮助信息都使用F1作为快捷键,而PowerGUI脚本编辑器也不例外。当我们在对应的cmdlet上按下F1时,PowerGUI脚本编辑器会打开一个新窗口,并为我们提供经过格式化后的帮助信息。

    clip_image007

    接着我们来介绍下PowerGUI脚本编辑器的调试功能。

    PowerGUI脚本编辑器提供了断点,运行到光标,执行所选部分,逐语句,输出窗口等高级调试功能。这些功能对经常编写脚本的人来说并不陌生,这里我就不在详细介绍了。

    下面要说的是PowerGUI脚本编辑器中的调试选项。

    clip_image008

    需要解释的是运行空间这个概念。这是一个对PowerShell进行开发时用到概念,那么对于我们使用者而言该怎么理解呢?

    首先要解释的是第一个选项“在同一运行空间中运行所有脚本”。我们在选中这一选项后,执行下面这样一行脚本。

    $a ="Test"

    在本地变量窗口中会出现$a这个变量,在输出窗口中则什么都没有。接着我们修改上面那行代码,仅保留$a。这时在执行脚本,在本地变量窗口中还是会出现$a这个变量,同时在输出窗口中会输出Test

    接着我们修改调试选项为“调试器每次启动时重设PowerShell运行空间”,然后重复以上的测试过程。在第二次运行后最终结果是在本地变量窗口和输出窗口都不会出现相关信息。

    而加载PowerShell配置文件这个选项则不难理解,比如在安装PowerTab后,PowerTab会往配置文件中写入相关信息,当我勾选该选项,并在PowerShell执行脚本时,在输出窗口中也会出现相关信息。

    clip_image009

    有关PowerGUI的介绍今天就暂时告一段落了,当然PowerGUI还有很多功能值得挖掘,以后如果有特别好的例子我会再次撰文向大家推荐的

发表于 作者 ghjconan | 0 评论
归档在:

PowerGUI介绍之扩展管理控制台

    在上次的PowerGUI介绍中,主要分析了PowerGUI 管理控制台的一些基本操作和管理控制台的基本组成元素。本周的PowerGUI介绍将通过两个例子来讨论如何扩展PowerGUI控制台。

  1. 显示磁盘剩余空间的相关信息

    在本例中我们将使用到[Local System]-[Drives]-[FileSystem]这个信息节点。先来让我们看看单击这个节点后所呈现的信息。

    clip_image001[9] image

    单击这个节点后,相关代码会枚举出本地系统上的相关驱动器,并在左侧节点树下动态添加相关节点。同时在中间的结果窗格中显示相关驱动器的信息。但是我们会发现这些信息的意义不大,接下来我们所要做的就是自定义“链接”操作来获得驱动器的剩余空间。

    PowerShell中没有现成的cmdlet来获得和驱动器剩余空间相关的信息,所以在添加新项目时所选的对象是“脚本链接”。

    clip_image002[9]

    面对这包括注释之内的聊聊数行文字,你是否觉得无所适从?如果你看过这篇PowerShell每周提示的话就知道在PowerShell下想要获得文件夹的大小是一件不容易的事,那要获得磁盘的相关信息岂不是难上加难?是的,如果你是一个纯正的PowerShell粉丝的话,这件事对你来说有点麻烦。当然如果你更关心结果的话,那就可以使用那篇提示最后脚本专家提及的作弊手段。让我们的老朋友FileSystemObject来帮助我们解决困难。

    既然有了思路,我们就可以在“荒原”进行开垦了。首先可以给这个操作起个名,比如显示磁盘剩余空间,然后删掉相关注释语句,使用以下代码在第一行新建FileSyetemObject的实例:

    $objFSO = New-Object -ComObject "Scripting.FileSystemObject"

    然后在ForEach循环中使用$objFSO的GetDrive方法配合 $_ 获得每一个驱动器对象,然后由驱动器对象的FreeSpace属性获得驱动器的剩余空间。到此为止的代码如下图所示:

    clip_image003[9]

    单击确定返回PowerGUI管理控制台主界面,然后单击操作窗格中刚刚新建的链接操作,结果窗格中将会显示以下信息:

    clip_image004[9]

    很好,我们得到了以字节显示的驱动器剩余空间。至少证明我们的思路是正确,而且再次证明当遇到麻烦时老朋友总是那么可靠。但是目前结果窗格中的显示结果显然无法满足我们。一是以字节数显示的剩余空间非常不直观,二是上篇介绍提到的难看的<Value>又出来招摇过市了。接下来的任务,美化显示结果, 我不用说大家也猜到了。

    先来解决第一个问题,以字节数显示的剩余空间。这个问题在这篇PowerShell每周提示中已经给出了解决方案。我们可以使用类似以下的代码来解决:

    $strFreeSpace = "{0:N2}" -f ($objDrive.FreeSpace / 1GB) + "GB"

    如果你看过相关PowerShell每周提示,那么上面这行代码应该不难理解。接下来我们要解决显示<Value>的问题。

    先来分析下问题的成因。由于我们的代码很简单,代码运行的结果是输出被格式化的数字,对PowerGUI来说,这当然只能被理解成 <值>,至于是什么对象的值,PowerGUI不会去关心。所以我们还要编写相关代码告诉PowerGUI这是哪个对象的值。

    这个问题是不是很困难?是的,在你不知道处理自定义对象和Add-Member cmdlet这篇每周提示之前。根据从自定义对象这篇提示中得到的帮助,PowerGUI中的最终代码如下:

    clip_image005[9]

    这里有几点要提示:1, $script:colList是用来存放所有驱动器剩余空间的变量,script是变量的作用范围,详细帮助信息可以使用 help about_Scope -full 命令获得。2, if语句帮助我们限定驱动器的类型。3, 最后使用Select-Object cmdlet显示相关属性。

    最后测试下,首先单击[Local System]-[Drives]-[FileSystem]这个信息节点后,选择结果窗格中的相关列表项,再单击操作窗格下我们新建的脚本链接,最后将会在结果窗格中显示以下内容:

    clip_image006[9]

    漂亮多了吧?

  2. 在PowerGUI中发起搜索

    刚才的那个脚本也许有点难度,考验了我们综合运用PowerShell每周提示中相关知识的能力。接下来介绍的这个例子相对来说简单点。

    对广大的ITPro而言,Event ID这组词是非常熟悉的,有时也许会让人感到害怕,比如当事件日志中出现了大量的红叉。这时我们也许会用Event ID作为关键字进行搜索进行排错。下面我们就来看看如何从PowerGUI提供的事件查看器中直接发起对Event ID的搜索。

    首先我们需要单击PowerGUI管理控制器中的[Local System]-[Event Logs]-[系统]这个信息节点。这时在结果窗格中就会列出系统事件。

    clip_image007[9]

    接下来我们要做的是在右侧的操作窗格的中新建一个脚本操作(Action-Script Action,注意和第一个例子的区别)。然后输入以下代码:

    clip_image008[9]

    和上个脚本一样,这里又使用了一个COM对象,我们只需要知道这个对象的Open方法能帮我们打开浏览器进行搜索(实际上就是在运行对话框中输入网址然后按回车)。而相关网址的获得对各位ITPro来说也很容易理解,我们需要做的就是将要搜索的的关键字用变量替换后嵌入到网址中以实现搜索功能。上面截图中使用的网址是EventID,当然我们可以换成Live Search等地址。

    这次就谈这么多,下次我们谈谈脚本编辑器。

发表于 作者 ghjconan | 3 评论
归档在:

PowerGUI 介绍之管理控制台

今天我们要介绍的主角是PowerGUI管理控制台PowerGUI的界面设计遵循了的MMC 3.0的风格。左侧是包含各种信息节点的树状结构,中间则是结果窗格,右侧则是操作窗格。

clip_image001

  • 树状结构

    • 文件夹节点和PowerPack

    在树状结构中,按照管理对象的不同,可以利用文件夹(Folder节点对PowerShell脚本进行分类管理。例如在上图中,PowerGUI的开发者已经定义了活动目录,网络,本地系统三类PowerShell脚本,当点击其中的任意节点时,就会执行该节点所包含的PowerShell脚本。PowerGUI的使用者也可以使用文件夹来整理自己的PowerShell脚本,同时PowerGUI也可以将用户自定义的脚本集合导出成类型为PowerPack的文件,方便PowerGUI的爱好者分享自己所写PowerShell脚本,导出方法是在具体节点上单击右键,在出现的快捷菜单中选择导出。

    clip_image002

    clip_image003

    需要注意的是,在接下来的另存为对话框中会出现一项名为“需要PowerShell管理单元”的选项,如果你的脚本中使用了和AD或者Exchange相关的Cmdlet时可以把此项勾上。当其它PowerGUI使用者在没有安装相应管理单元的机器上导入PowerPack时,PowerGUI会提示用户安装相应的管理单元:

    clip_image004

    而如果不勾选相应的选项,其他PowerGUI使用者可以在没有安装相应管理单元的计算机上导入PowerPack,但是当具体展开一个节点时,就会出现报错信息:

    clip_image005

    我相信,谁都不愿意看到自己辛辛苦苦做好的PowerPack在给别人分享时,因为自己在导出时的一个小疏漏而造成其他人不必要的误会吧。

    当然PowerGUI安装程序自带的PowerPack还是比较少的,而且安装程序会根据你系统的情况来判断是否安装相应的PowerPack。(要开启针对ADPowerPack,需要从这里下载相应的管理单元进行安装)

    clip_image006

    我们可以从PowerGUI 网站下载国外的PowerShell爱好者搜编写的PowerPack。其中有针对Hyper-VPowerPack,有兴趣的朋友不妨下载下来用用。

    • 节点和脚本节点

    除了文件夹节点之外,还有两种类型的节点,分别名为节点(Node),脚本节点(Script Node。可以通过快捷菜单创建这两种类型的节点。

    clip_image007

    节点和脚本节点的区别可以通过以下两张图来区分。

    节点:

    clip_image008

    从图上不难看出,节点为我们提供了一个图形界面来选择相应的cmdlet和参数来获得所需的结果。在界面中可以通过使用“搜索”框来快速找到所要用的cmdlet。而下方提示复选框的作用是,当用户单击节点后,PowerGUI在执行相应的cmdlet前会给出一个提示,让用户确认参数是否正确及是否需要更改。

    clip_image009

    使用者可以通过修改参数来得到不同的结果。例如使用者想知道C:\Windows下有多少个.ini文件而不是.txt文件,那么只需修改Filter参数为*.ini即可。

    脚本节点:

    clip_image010

    而脚本节点则为我们提供了一个简化版的脚本编辑器,在这里可以充分发挥大家的想象力,编写你所需的PowerShell代码。需要注意的是脚本必须以“get-"开始的cmdlets或者write-output cmdlet作为结束。

     

  • 结果窗格

    clip_image011

    结果窗格中,需要注意的是位于顶部的筛选器及下方的PowerShell代码。

    筛选器顾名思义是对已得到的结果再进行进一步筛选。例如我们想要查询事件号为4226的系统事件,则可以通过以下操作获得。

    在左侧的树状结构中选择Local System,然后再选择Event Logs,最后选择系统。这时在结果窗格中会列出100条最新的事件日志(为什么是100条?稍后再公布)。

    clip_image012

    此时,我们可以单击筛选器,然后让筛选器为我们筛选出,EventID4226的事件,具体配置如下:

    clip_image013

    单击应用后,我们会在结果窗格中得到相关事件的详细信息。

    clip_image014

    注:筛选器的操作符中文翻译有点问题,带来的不便,请大家见谅。Wiki上的相应条目已更新,等待PowerGUI开发团队的更新。

    前面我卖了一个小关子,说结果窗格中列出的事件记录为100条,下面就来看看,我为什么能这么肯定的说是100条。单击“PowerShell代码”标签,你会发现刚才的筛选操作实际上是执行了以下的PowerShell代码

    clip_image015

    看到Get-EventLog cmdlet和它的参数Newest了吧,这就是我那么有信心的原因。通过这个关子我想各位也知道“PowerShell代码”标签是什么用途了吧。

     

  • 操作窗格

    最后需要介绍的是操作窗格。操作窗格被定义成三栏,分别是链接(Links操作(Actions常规操作(Common Actions。下面我们通过Local System下的Processes节点对这三种操作进行介绍。

    clip_image016

    • 链接

    链接提供了列表中所选对象和与它们相关对象间的联系,例如与选择邮箱所关联的用户,所选文件的相应权限,链接允许你获得更多的信息。对进程而言,PowerGUI自带的一个链接是Associated DLLs,该链接的作用是获得进程中加载的相关DLL信息。我们可以选中一个或多个进程来查看下关信息,这里我就选定列表中的第一个进程,然后单击操作窗格中链接下的Associated DLLs获得如下图所示信息:

    clip_image017

    当然,各位肯定对怎么获得这一信息的PowerShell代码比较感兴趣。通过之前的介绍,你已经知道使用结果窗格中的PowerShell代码标签可以查看相关的PowerShell语句。这里我们换一种方法,首先通过上方的导航栏回到Processes节点,单击单击操作窗格中链接下的Associated DLLs右侧的下拉箭头,在弹出的菜单中选择属性。随后我们就能在弹出的窗口中看到Associated DLLs的相关代码:

    clip_image018

    这里的$input代表在操作窗格中选中的对象,之后通过管道将对象传递ForEach-Object cmdlet,而使用的Foreach的理由也很容易理解,因为$input可能包含一个以上的对象。剩下的就是在循环语句中显示$input中每个对象的Modules属性,$_则代表在当前管道中的对象。

    通过这个PowerGUI自带的示例,我们可以自行添加链接操作。相关操作也很简单,单击链接下的“添加新项目”按钮,选择脚本链接:

    clip_image019

    和添加节点有点类似,链接针对现成的cmdlet并提供GUI界面来输入参数,而脚本链接允许使用者自己编写PowerShell代码,而导入则是将以前导出的链接添加进来(导入操作可以在相应的链接按钮上执行)。

    这里我已经添加好名为命令行的链接,该链接的作用是获得进程的命令行参数,换句话说就是可执行文件路径。而代码和Associated DLLs的代码基本一致,只需将Modules替换成Path即可,下面是选中列表中的第一个进程,单击命令行后的结果:

    clip_image020

    这里需要提醒的一点是,如果你和我一样是在Vista下运行PowerGUI的话,注意要“以管理员身份运行”PowerGUI,否则有些进程你是获取不到相关执行文件的路径,同时PowerGUI也不会报错来提示你提升权限。

    至于那个有点难看的<Value>就留到下次我们讨论更复杂的自定义链接操作时再说吧。

    • 操作

    所谓操作就是针对结果窗格中的对象所要采取的相关命令,比如我们可以对进程中的记事本进程执行停止操作,也就是常说的结束进程。如果结果窗格中的对象是服务,我们则可以使用停止服务,暂停服务等操作。这里还是以结果窗格中的对象为进程来举例。

    首先我们启动记事本程序,然后利用筛选器功能找到记事本进程。

    clip_image021

    这时我们单击右侧操作栏下的Stop来停止该进程。而隐藏在Stop这个按钮后的PowerShell代码或者说是cmdlet则是Stop-Process。和链接相似,操作也分使用cmdlet的操作,和使用PowerShell代码的操作。在上面的截图中除了Stop以外的操作都是有PowerShell代码实现的,这里就不再赘述,又兴趣的朋友可以看看相关代码。

    • 常规操作

    最后我们简单提一下常规操作。通过观察PowerGUI自带的常规操作,我们不难发现这些操作都是将结果窗格中生成的信息导出成不同的格式进行保存。

    clip_image022

    这里之所以要简单介绍的一个原因是因为其中的代码涉及到.Net的相关内容,下面的截图是Report as XML的相关代码:

    clip_image023

    代码第一段的作用是在PowerGUI中显示参数窗口,第二段则是对文件是否以存在进行判断,并给出提示由用户选择是否覆盖已存在文件,最后没有出现在的截图中的则是保存文件的代码,利用了现成的export-xml cmdlet。当然这些代码是可重用的,如果你需要编写相关的常规操作可以利用现成的代码。

    今天说了一大堆界面操作上的内容,也许有的朋友会觉得枯燥,但所谓“工欲善其事,必先利其器”,只有对PowerGUI界面上一些概念有所了解以后,我们才可以尝试着定制属于自己的PowerGUI。正如最后的“Report as XML”的实际代码一样,一些PowerGUI的高级应用会涉及到.Net,所以更需要对PowerGUI有深入的了解。

    好了,今天就到这里,下次我们要解决今天遗留的一个问题,如何处理下图中那个难看的Value

    clip_image020

  • 发表于 作者 ghjconan | 0 评论
    归档在:

    PowerGUI——学习PowerShell的好帮手

    看过我所写的 PowerShell 笔记 的朋友对PowerGUI这款软件一定不会陌生,这个让我爱不释手的第三方PowerShell增强软件最近更新到了1.5.3。在今年六月份,PowerGUI网站推出了本地化PowerGUI的Wiki项目。八月底的时候我向PowerGUI的缔造者Dmitry Sotnikov申请PowerGUI界面的简体中文化,在九月之前整个简体中文化工作已基本完成(其中有一部分文本来自PowerShell中文版的帮助)。虽然简体中文界面在1.5.2中已经被集成进去,但是由于程序在处理助记符(&)时有一点问题,所以我迟迟没有向诸位具体介绍。

    现在在1.5.3中这个问题已经被解决了,所以我非常高兴的向诸位再次推荐这款出自名门(Quest)的第三方PowerShell增强软件。

    PowerGUI有两大部分组成,一是下面这张图中的管理控制台。

    第二部分就是我经常使用的脚本编辑器了。

    接下来的一段时间里,我会向大家详细介绍如何使用PowerGUI。

    诸位可以从这里 http://powergui.org/downloads.jspa 下载最新版本的PowerGUI。

    如果在使用中文界面时存在任何让你觉得有疑惑的地方请切换到英文界面,然后把你认为需要改进和纠正的地方,发邮件至ghjconan#itecn.net(联系时请把#替换成@,仅回复和PowerGUI有关的问题),我会把有关意见和建议更新到相应的Wiki,然后请大家耐心等待PowerGUI开发团队在下一次更新时解决这些问题。

    最后让我们来欣赏下PowerShell领域大师们的风采。

    从左往右数,第四位就是PowerGUI的缔造者Dmitry Sotnikov(MVP Profile),右边两位就是PowerShell的缔造者Jeffrey Snover和PowerTab的缔造者MOW.

    发表于 作者 ghjconan | 4 评论
    归档在:

    一次moveuser的使用经历

    有一段时间没再ITECN上更新了,主要因为工作上要完成OCS的SOP文档,所以把主要精力都投入在上面了。

    前昨两天接到一个任务,需要帮助一些没有加域的计算机加域,并迁移用户的相关设置。经过接近一天时间的努力,用脚本配合moveuser,顺利完成了工作组下的用户平滑迁移到域用户,收获颇多,在此和诸位分享。也许在编写脚本的过程中,某些地方还有欠缺,还请各位多多包含。

    先来描述下问题情境:
    1. 用户使用本地计算机Users组成员帐号(假定用户名为user)登录计算机,并且使用了一段时间,用户配置文件偏大。且本地管理员密码不统一,可选的密码有三个。已分配IP地址。
    2. 现在计划将这些用户批量加域,计算机需要重命名,并迁移用户的相关设置。
    3. 整个过程对用户产生的影响越小越好,Helpdesk要进行的操作越少越好。
    4. 所有脚本最好整合成WinRAR自解压包。

    在描述具体实现过程前,诸位可以下载以下视频,查看最终效果。

    从 SkyDrive 下载

    下面来说说具体解决步骤。

    首先第一个问题是,用户是以本地计算机Users组成员身份登录计算机的,在这种情况下,我们是没有办法使用他的用户来加域的,需要使用本地管理员组里的成员,如Administrator才行。但是这又引出另一个问题,管理员帐号密码不统一,我们需要使用一种方法使得脚本能遍历这三种可能性,最终用正确的密码以管理员身份运行重命名计算机的脚本。如果三个密码都无法使用,那只能给个提示劳驾Helpdesk的兄弟破解密码了。

    那么如何实现以上需求呢?VBScript当然无法实现,他没有提供内置的方法来让我们以其它用户身份运行程序或脚本,而且遍历管理员密码也很难实现。那么我们就只能面对出师未捷身先死的窘境了么?当然不是,这个世界如此丰富多彩,早已有高人给我们提供了其它工具来实现这些需求,下面请出的是AutoIT,先来看看最终代码:

       1: dim $pwd[3]
       2: $pwd[0] = "pass@w0rd1"
       3: $pwd[1] = "pass@w0rd2"
       4: $pwd[2] = "pass@w0rd3"
       5:  
       6: For $i = 0 to 2
       7:         $retVal = RunAs("Administrator",@ComputerName,$pwd[$i],1,"wscript.exe " & Chr(34) & @ScriptDir & Chr(34) & "\renamecomputer.vbs")
       8:         If $retVal = 0 Then
       9:             if $i+1 = 3 Then
      10:                 MsgBox (0+16,"错误","管理员密码错误,请重设本地管理员密码。")
      11:                 ExitLoop
      12:             EndIf
      13:         Else
      14:             ExitLoop
      15:         EndIf
      16: Next

    有关AutoIT语法的相关内容,我在这里就不在阐释了,有兴趣的朋友可以去找找相关资料。在以上代码中,我们首先声明一个数组来存放可能的管理员密码。接着我们使用For循环来遍历可能的管理员密码,如果三个已知密码都是错误的,那么我们会给Helpdesk以下提示: 

    如果三个密码中有一个正确,就会启动重命名计算机脚本(与AutoIT生成的程序在同一路径下,所以使用@ScriptDir常量,并且为了避免路径中的空格引发错误,所以使用了Chr(34)。而路径中有空格是因为将涉及到的文件都解压到%temp%目录下)

    下面来说说重命名计算机的脚本。

       1: Set objShell = CreateObject("Wscript.shell")
       2: Set objFSO = CreateObject("Scripting.FileSystemObject")
       3:  
       4: strComputerName = InputBox("请输入新计算机名","输入")
       5: strLocalUserName = InputBox("请正确输入当前用户名" & vbcrlf & "否则将影响到用户配置文件迁移!","输入")
       6: strDomainUserName = InputBox("请正确输入域账号,姓名的拼音,比如lisi" & vbcrlf & "否则将影响到用户配置文件迁移!","输入")
       7: strScriptpath = Replace(WScript.ScriptFullName,WScript.ScriptName,"")
       8:  
       9: Set objTXT = objFSO.CreateTextFile(strScriptpath & "\moveuser.bat")
      10: objTXT.WriteLine("moveuser " & strLocalUserName & " contoso\" & strDomainUserName & " /k")
      11: objTXT.Close
      12:  
      13: objShell.Run("cmd /c net user administrator abcd@123")
      14:  
      15: WScript.Sleep 1500
      16:  
      17: strComputer = "."
      18: Set objWMIService = GetObject("winmgmts:" _
      19:     & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
      20:  
      21: Set colComputers = objWMIService.ExecQuery _
      22:     ("Select * from Win32_ComputerSystem")
      23:  
      24: For Each objComputer in colComputers
      25:     strErr = objComputer.Rename(strComputername)
      26: Next
      27:  
      28: If strErr = 0 Then
      29:         objFSO.CopyFile strScriptpath & "\joindomain.vbs","c:\windows\system32\"
      30:         objFSO.CopyFile strScriptpath & "\moveuser.bat","c:\windows\system32\"
      31:         objFSO.CopyFile strScriptpath & "\moveuser.exe","c:\windows\system32\"
      32:         objshell.RegWrite "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce\joindomain", "c:\windows\system32\joindomain.vbs","REG_SZ"
      33:         MsgBox "单击确定重新启动计算机",vbOKOnly+vbInformation,"信息"
      34:         objshell.Run("cmd /c shutdown.exe -r -t 0")
      35: End If

    首先我们需要Helpdesk输入新计算机名,当前用户的登录名,将要使用的域账号名,这也是整个迁移过程中唯一需要用户输入的地方。当然这里使用简单的输入框会存在一点问题,如果单击输入框中的取消按钮,可能会导致整个加域任务失败。不过这个任务是由Helpdesk完成而不是普通用户,所以我们暂时不处理这个问题。接下来,我们取得当前脚本的运行路径,并在该路径下根据Helpdesk的输入信息来生成moveuser.bat供后续使用。接着我们需要做的是重设本地管理员的密码,当下一次重启时,Helpdesk需要使用管理员帐号登录,加域脚本会自动执行加域脚本和moveuser.bat。

    接下来我们利用脚本中心提供的示例代码重命名计算机,并将加域脚本,包含moveuser命令的批处理及moveuser命令本身复制到system32目录下。同时我们向注册表里写入相关信息,使得下次登录时加域脚本能自动运行。最后脚本执行完成,然后单击确定后重新启动计算机。

    重启完成后,Helpdesk将使用管理员帐号登录计算机,计算机会自动执行加域脚本joindomain.vbs。

    加域脚本中的代码也不太复杂,下面先贴上代码:

       1: Const JOIN_DOMAIN = 1
       2: Const ACCT_CREATE = 2
       3:  
       4: strdnsOne = "192.168.100.1"'InputBox("Please type your first DNS address","Input")
       5: strdnsTwo = "192.168.100.2"'InputBox("Please type your second DNS address","Input")
       6: strDomain = "contoso.com"'InputBox("Please type your domain name","Input")
       7: strUser = "administrator"'InputBox("Please type your domain user name","Input")
       8: strPassword = "abcd@123"'InputBox("Please type you domain user's password","Input")
       9:  
      10: strComputer = "."
      11:  
      12: Set objShell = CreateObject("Wscript.shell")
      13: Set objFSO = CreateObject("Scripting.FileSystemObject")
      14:  
      15: Set objWMIService = GetObject("winmgmts:" _
      16:     & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
      17:  
      18: Set colNetCards = objWMIService.ExecQuery _
      19:     ("Select * From Win32_NetworkAdapterConfiguration Where IPEnabled = True")
      20:  
      21: For Each objNetCard in colNetCards
      22:     arrDNSServers = Array(strdnsone, strdnstwo)
      23:     objNetCard.SetDNSServerSearchOrder(arrDNSServers)
      24: Next
      25:  
      26: Set objNet = CreateObject("Wscript.network")
      27: strComputerName = objnet.ComputerName
      28:  
      29: Set objComputer = GetObject("winmgmts:{impersonationLevel=Impersonate}!\\" & _
      30: strComputer & "\root\cimv2:Win32_ComputerSystem.Name='" & _
      31:         strComputername & "'")
      32:  
      33: ReturnValue = objComputer.JoinDomainOrWorkGroup(strDomain, _
      34:     strPassword, strDomain & "\" & strUser, NULL, _
      35:         JOIN_DOMAIN + ACCT_CREATE)
      36:  
      37: If ReturnValue = 0 Then
      38:     Set objCMD = objshell.Exec("c:\windows\system32\moveuser.bat")
      39:     Do While objCMD.Status = 0
      40:         WScript.Sleep 500
      41:     Loop
      42:     MsgBox "单击确定重新启动计算机",vbOKOnly+vbInformation,"信息"
      43:     objshell.Run("cmd /c shutdown.exe -r -t 0")
      44: End If

    首先声明加域脚本要用到的常量,接着我们将加域所用到的dns服务器地址,域名,加域用的帐号名和密码(实验环境中直接用的域管理员帐号,实际环境中我们当然不能用,可以使用一个专门的帐号来代替)存放在变量中。下面使用的代码来自脚本中心,我们只需根据具体环境相应修改即可。当加域成功后,脚本会运行之前创建的moveuser批处理来迁移用户配置文件,当迁移完成后,需要再次重启计算机。至此整个任务已经完成,接下来需要做的就是等待计算机重启完成后,使用域账号登录,验证是否达到了我们预想的效果。 
      

    总结几点:

    1. 一开始的输入是关键,在使用这整套脚本之前,我们需要和Helpdesk强调这一点
    2. 我们需要保证软硬件环境能使得加域成功,现有脚本中没有做任何处理
    3. 用WinRAR生成自解压包时要注意解压路径的选择(start.exe即是用AutoIT写得脚本) 


    4. 关于虚拟机环境。诸位注意,我用的是Windows Server 2003做的实验,但是如果你在Windows Server 2003中创建普通帐号,这个账号是没有办法是没有重新启动计算机的,所以记得在本地安全策略里修改关闭系统的权限。

    最后还是强调一句:

    文中所有代码仅供参考,如需测试请在虚拟机中执行。本文的目的旨在提供思路而非最终解决方案。

    发表于 作者 ghjconan | 10 评论
    归档在:

    Windows PowerShell每周提示(47):你有所不知的关于Windows PowerShell函数的三件事

    什么?你说你了解在Windows PowerShell中使用函数时所涉及的方方面面的事?好的,对你来说这很好。但是,只是为了以防万一,你也许需要看看脚本专家所知的在Windows PowerShell中使用函数时的三件事。

    你能为函数参数设置默认值

    作为一般惯例,当调用函数时你会传递给它数量适当的参数。例如,假设我们有以下函数,该函数的功能是将两数相乘:

    function test ($x, $y)

    {

    $x * $y

    }

    为了使用这个函数,你一般会在调用函数时为变量$x和$y提供相应的值:

    test 33 22

    这很好,这是你在调用函数时应当做的。然而,考虑下下面的函数,它将根据一个给定的日期来返回这个日期是一年中的第几天:

    function test ($x)

    {

    (Get-Date $x).DayOfYear

    }

    为了调用这函数,你会使用类似以下的命令:

    test "6/27/2008"

    到目前为止一切尚可。另一方面,假设你时常需要知道当前日期是一年中的第几天。那么从理想情况上来说,你可以仅仅调用不带任何参数的函数,并且在默认情况下,函数将会返回当前日期是一年中的第几天:

    test

    那么你如何在函数中设置默认值呢?为什么这么问,当然像这样:

    function test ($x = (Get-Date).Date)

    {

    (Get-Date $x).DayOfYear

    }

    如你所见,我们所做的是向函数参数$x预设一个值。特别是,我们将当前日期(使用Get-Date cmdlet)的Date属性值赋值给该变量。如果当我们调用此函数时提供了一个参数那么$x将被赋予该参数值。然而,如果当我们调用此函数时没有提供参数那么$x将会被赋予默认值:当前日期。试试这两个命令,看看你会得到什么结果:

    test "6/27/2008"

    test

    顺便提一句,你能向函数变量分配一个特定的数据类型。例如,在下面的函数中,我们将数据类型整数([int])赋值给函数变量$x和$y:

    function test ([int] $x = 2, [int] $y = 8)

    {

    $x * $y

    }

    现在,当我们使用以下命令调用该函数时你认为将得到什么结果:

    test 4.332 3.7181

    对的:我们得到了12这个值。为什么?因为PowerShell在将两个参数相乘之前会转换每个参数值(4.332 and 3.7181)为整数(分别为4和3)。你也注意到我们将$x和$y的默认值设为2和8。假设我们调用此函数时不提供任何参数。在这种情况下我们会得到16,因为函数将会把默认值2和8相乘起来。(当发现有人注意到我们时,我们总是感到惊讶!)现在,当我们使用这个命令时将会得到什么结果呢:

    test 11

    如果你回答88,那么给你自己一颗星星作为奖励吧。(不,不是一颗金色的星星,只是一颗星星。这不是一个很难的问题。)为什么是88?好的,有一件事是肯定的,参数值11将被赋值给$x,提供给函数的第一个参数总是被赋值给$x。因为我们没有提供第二个参数,所以$y将会是默认值8。因此11乘8将是……对,88。没关系:给你自己一颗金色的星星吧,你应得的。

    注意。如果我们提供三个(或更多)的参数给这个函数那会怎么样?好的,不会发生什么不好的事:函数将会忽略额外的参数,并且只将参数1和参数2相乘。然而,这些参数不会消失和被遗忘:所有的“额外”参数值将被储存在变量$args中。这意味着这些值依然可用,如果你将来用得着它们的话。

    顺便,还记得我们说过“…提供给函数第一个参数总是被赋值到$x”么?好的,这句话只是部分正确。事实证明,如果你使用已命名的参数调用你的函数,那么你能指定什么样的值会被赋值给某一个函数变量,而不用担心提供这些参数时的顺序。

    问得好:这到底是什么意思?好的,让我们看下另一个函数:

    function test

    {

    param ($x = 5, $y = 14)

    $x * $y

    }

    这里我们使用param语句来声明两个函数变量及他们的默认值。注意param语句被包含在相关函数的脚本块中。假设,我们使用下面的命令调用该函数:

    test 4

    这样的话,4将被赋值给$x且$y将会使用默认值14。作为结果,函数返回56这个值。

    但是如果我们想要把4赋值给$y,那会怎么样?很好,我们需要在调用函数时说明这一点:

    test –y 4

    明白了?在本例中,我们明确将4赋值给$y。这一次$y将会等于4。$x将会是默认值5,函数将会返回20。很酷吧?如果你想要对两个变量赋值,那么,就对两个变量赋值而不需要担心赋值的顺序:

    test –y 4 –x 22

    你能通过管道向函数传递数据

    当你第一次启动Windows PowerShell时是不是有人这样对你说,“只有cmdlet才能在管道末接收数据。”这是什么意思?好的,对PowerShell的新人来说,他们会经常写类似以下的命令,认为这个命令将会把当前日期和时间赋值给变量$x:

    Get-Date | $x

    然而,该命令不会把当前日期和时间赋值给$x,取而代之的是,它将会导致一个令人害怕的错误消息:

    Expressions are only permitted as the first element of a pipeline.

    At line:1 char:14

    + get-date | $x <<<<

    你得到这个错误消息是因为变量($x)位于管道的接收端,这是不被允许的。你必须在管道的接收端放置一个cmdlet。

    至少当你第一次使用Windows PowerShell时他们是这样说的。现在,你有一点资历了,是时候学习生活的真实面了:当你知道这个小技巧后,你会在管道的接收端放置一个函数。

    那么我们正在谈论的这个技巧是什么呢?该技巧和$input变量有关,这是一个保存所有流经管道数据的自动化变量。让我们看一个能在管道的接收端使用的函数:

    function test

    {

    foreach ($i in $input)

    {$i.ToUpper()}

    }

    如你所见,第一个命令是一个标准的foreach循环。但是仔细看看我们所循环的内容。我们循环$input内的值。然后,对每一个值,只是使用ToUpper方法来将值转换为大写。

    换句话说,假设我们运行以下命令:

    "a", "b", "c" | test

    下面就是我们得到的结果:

    A

    B

    C

    你能在其它范围内使得函数有效

    好的,你已经知道了。毕竟,你能使得一个函数成为全局函数(也就是说,在其它范围内有效)通过在你的PowerShell配置文件或者在“dot sourcing”中包含函数。但是还有一种方法能使得函数在全局生效,只需要在函数名前加上关键词global

    function global:test ($x, $y)

    {

    $x * $y

    }

    就是这样。

    奖励提示:查看全局函数的代码

    知道一个名为test的函数,但是你不能想起该函数到底是干什么的?万一遇到这种情况,使用Get-Content cmdlet来提取函数代码:

    Get-Content function:\test

    英文原文

    http://www.microsoft.com/technet/scriptcenter/resources/pstips/jun08/pstip0627.mspx

    PS

    转眼之间,2008年的十一黄金周马上就要结束了,而Windows PowerShell每周提示的系列翻译也在今天画上了句号。

    感谢脚本专家们为我们带来的这系列文章。有关Windows PowerShell的其它介绍文章,诸位可以关注下TechNet Magazine上Don Jones所撰写的专栏。

    发表于 作者 ghjconan | 0 评论
    更多内容 下一页 »