1. Trang chủ
  2. » Công Nghệ Thông Tin

Professional Windows PowerShell Programming phần 4 docx

34 824 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 34
Dung lượng 651,14 KB

Nội dung

In this case, output ofget-childitem *.txtcontains a property namedFullNameas shown below, which is bound to thepathparameter ofthetouch-filecmdlet: PS C:\user\gxie> get-childitem *.txt

Trang 1

}set{number = value;

}}

protected override void ProcessRecord()

{

}

}

Now try running theGet-EvenNumbercmdlet with an odd argument value for theNumberparameter:

PS C:\user\gxie> get-evennumber -number 13

Get-EvenNumber : Cannot validate argument on parameter ’Number’ Not an even num

In cmdlet development, parameters can be defined as any NET types, from simple types such asstring,

intto complicated types such asSystem.Process One common task, however, is to design the cmdlet

so that parameter values can be easily typed in from the command line

For example, assume that you want to develop a cmdletUnite-Rectangle, which calculates the union of

Rectangle rectangle1 = new Rectangle(0,0,0,0);

[Parameter(Mandatory = true, Position = 1)]

public Rectangle Rectangle1

80

Trang 2

Rectangle rectangle2 = new Rectangle(0, 0, 0, 0);

[Parameter(Mandatory = true, Position = 2)]

public Rectangle Rectangle2

You can see that both parametersRectangle1andRectangle2have the typeSystem.Drawing

Rectangle To specify rectangle values for these command parameters, you would have to create

rect-angle objects first (using thenew-objectcmdlet) and then pass them to the new cmdlet, as shown in thefollowing example:

PS C:\user\gxie> $r1 = new-object system.drawing.rectangle 1,2,1,1

PS C:\user\gxie> $r2 = new-object system.drawing.rectangle 3,4,1,1

Trang 3

Top : 2

Bottom : 5

IsEmpty : False

The first command creates a rectangle object with the left-bottom corner set to (1,2) The second command

creates a rectangle object with the left-bottom corner set to (3,4) Both rectangles have a width and height

of 1 After the rectangles are united, the smallest rectangle that can cover them both has a left-bottom

corner of (1,2), with a width and a height of 3 The math works correctly, but having to create two

rect-angles beforehand is not desirable

It would be nice to allow users to type a list, a string, or a hash table from the command line, which you

would automatically convert into rectangles To achieve this, parameter transformation comes in handy.

Basically, a custom parameter transformation attribute can be defined with logic to convert parameter

values from one format (for example, list) to another format (for example, rectangle) Then the attribute

can be associated with a parameter so that this kind of transformation is done automatically during

if (input is IList){

IList list = input as IList;

if (list.Count == 4){

return new Rectangle((int)list[0], (int)list[1],

(int)list[2], (int)list[3]);

}}return inputData;

}

}

In the preceding example, you can see that this class is derived from the

ArgumentTransformation-Attributeclass The bulk of the work for this class is overriding theTranformmethod to transform

parameter values from one format to another Inside theTranformmethod, transformation is done

82

Trang 4

selectively More explicitly, you create a newrectangleobject only ifinputDatais a list of four

inte-gers Otherwise,inputDatawill be passed through as it is This implementation is chosen so that you

don’t mistakenly convertinputDataif it is already aRectangle In addition, passing throughinputData

allows another parameter transformation attribute down the chain to also process the data

Now, use this attribute in theUnite-Rectanglecmdlet:

[Cmdlet("Unite", "Rectangle")]

public class UniteRectangleCommand : PSCmdlet

{

Rectangle rectangle1 = new Rectangle(0,0,0,0);

[Parameter(Mandatory = true, Position = 1)]

Rectangle rectangle2 = new Rectangle(0, 0, 0, 0);

[Parameter(Mandatory = true, Position = 2)]

Trang 5

In addition, you can verify that directly passing aRectangleobject into either parameter will continue

In summary, this section described how to declare a parameter to be mandatory and positional, how to

use parameter sets, and how to validate and transform parameter values In next section, you will learn

how to make a parameter take pipeline input values

Processing Pipeline Input

One of most popular PowerShell features is pipelining objects from one command to another command

For a cmdlet to be used in a pipeline, it needs to be able to handle pipeline input and generate pipeline

output In this section, you will learn techniques to handle pipeline input in a cmdlet

PowerShell cmdlets can bind pipeline input to a parameter and access the parameter in the

Process-Record() method of the cmdlet The following example extends thetouch-filecmdlet:

[Cmdlet("Touch", "File", DefaultParameterSetName = "Path")]

public class TouchFileCommand : PSCmdlet

}set{fileInfo = value;

}}

84

Trang 6

protected override void ProcessRecord()

Comparing the preceding code with the example from the ‘‘Parameter Validation’’ section, the only

change here is setting theValueFromPipelineparameter totruefor theFileInfoparameter This

informs the PowerShell engine that the parameterFileInfowill bind to pipeline input in case it is not

specified from the command line

Use the following to run this cmdlet:

PS C:\user\gxie> get-childitem *.txt | Touch-File -date 7/1/2007

PS C:\user\gxie> get-childitem *.txt

Directory: Microsoft.PowerShell.Core\FileSystem::C:\user\gxie

In the first command of the preceding example, for each output object fromget-childitem*.txt, the

PowerShell engine will bind theTouch-Filecmdlet’sFileInfoparameter and call itsProcessRecord()

method to update the timestamp of the file

Cmdlets parameter can also bind to a property of a pipeline input object Following is an example that

binds thepathparameter to a property of the pipeline input object:

[Cmdlet("Touch", "File", DefaultParameterSetName = "Path")]

public class TouchFileCommand : PSCmdlet

{

private string path = null;

[Parameter(ParameterSetName = "Path", Mandatory=true, Position=1,

ValueFromPipelineByPropertyName = true)]

[Alias("FullName")]

[ValidateNotNullOrEmpty]

Trang 7

public string Path

{

get{return path;

}set{path = value;

}}

private FileInfo fileInfo = null;

[Parameter(ParameterSetName = "FileInfo", Mandatory = true, Position = 1)]

public FileInfo FileInfo

{

get{return fileInfo;

}set{fileInfo = value;

}}

DateTime date = DateTime.Now;

[Parameter]

public DateTime Date

{

get{return date;

}set{date = value;

}}

protected override void ProcessRecord()

{

if (fileInfo != null){

fileInfo.LastWriteTime = date;

}

if (File.Exists(path)){

File.SetLastWriteTime(path, date);

}}

}

86

Trang 8

In this example, instead of letting theFileInfoparameter take its value from the pipeline, you set

TakeValueFromPipelineByPropertyNameto betruefor the parameterpath Furthermore, you define

the aliasFullNamefor the parameterpath Now, if the pipeline input object has either apathproperty or

aFullNameproperty, then that property value will be bound to thepathparameter

Run thetouch-filecommand much as you did before:

PS C:\user\gxie> get-childitem *.txt | Touch-File -date 7/1/2007

PS C:\user\gxie> get-childitem *.txt

Directory: Microsoft.PowerShell.Core\FileSystem::C:\user\gxie

You can see that the timestamp of both txtfiles are updated In this case, output ofget-childitem

*.txtcontains a property namedFullName(as shown below), which is bound to thepathparameter ofthetouch-filecmdlet:

PS C:\user\gxie> get-childitem *.txt | get-member -membertype property

TypeName: System.IO.FileInfo

-Attributes Property System.IO.FileAttributes Attributes {get;set;}

CreationTime Property System.DateTime CreationTime {get;set;}

CreationTimeUtc Property System.DateTime CreationTimeUtc {get;set;}

Directory Property System.IO.DirectoryInfo Directory {get;}

DirectoryName Property System.String DirectoryName {get;}

IsReadOnly Property System.Boolean IsReadOnly {get;set;}

LastAccessTime Property System.DateTime LastAccessTime {get;set;}

LastAccessTimeUtc Property System.DateTime LastAccessTimeUtc {get;set;}

LastWriteTime Property System.DateTime LastWriteTime {get;set;}

LastWriteTimeUtc Property System.DateTime LastWriteTimeUtc {get;set;}

Pipeline Parameter Binding

Cmdlet parameters can be bound either to command arguments from the command line or to input

objects from the pipeline Command-line parameter binding happens once for each cmdlet invocation

It is performed before theBeginProcessing()method of the cmdlet implementation class is called

Trang 9

Conversely, pipeline parameter binding happens once for each pipeline input object It is performed

before each call to theProcessRecord()method of the cmdlet class

Similar to command-line parameter binding, pipeline parameter binding also needs to pick the parameter

to bind from valid parameter sets It uses the following process to decide which parameter to bind first:

1. Prepare parameter lists:Unbound pipeline parameters from valid parameter sets are

organized into two lists: One list (let’s call itValueFromPipeline) is for pipeline parameters

taking pipeline input (i.e., theValueFromPipelineproperty of the parameter attribute is set

to true); another list (let’s call itValudFromPipelineByPropertyName) is for pipeline

parame-ters taking pipeline input by property name (i.e.,ValueFromPipelineByPropertyNameis set

to true) Pipeline parameters from default parameter sets are put at the beginning of these

two lists so that they are considered for binding first

2. Bind next parameter:The pipeline parameter binder uses four steps to determine which

parameter to bind:

a. Bind parameters from theValueFromPipelinelist with no type conversion In this

step, if one parameter from the list has exactly the same type as pipeline input object,then it will be bound Otherwise, parameter binding goes to the next step

b. Bind parameters from theValueFromPipelineByPropertyNamelist with no type

con-version In this step, if one parameter from the list matches a property’s name of thepipeline input object and the parameter type matches the property type, then thisparameter will be bound Otherwise, parameter binding goes to the next step

c. Bind parameters from theValueFromPipelinelist with type conversion In this step,

if the pipeline input object can be converted into a type of parameter in the list, thatparameter will be bound Otherwise, parameter binding goes to the next step

d. Bind parameters from theValueFromPipelineByPropertyNamelist with no type

con-version In this step, if the name of one property of the pipeline input object matches

a parameter in the list and the property type can be converted to the parameter type,this parameter will be bound

3. Narrow down valid parameter sets:If there is a parameter bound in the preceding steps,

then parameter sets for the parameter bound will be used for narrowing down valid

param-eter sets Then the pipeline binder will recalculate the unbound pipeline paramparam-eter list and

bind the next parameter This process will continue until no parameter can be bound

To illustrate the process of pipeline parameter binding, let’s expand thetouch-filecmdlet to specify

that bothPathandFileInfotake their value from the pipeline:

[Cmdlet("Touch", "File", DefaultParameterSetName = "Path")]

public class TouchFileCommand : PSCmdlet

{

private string path = null;

[Parameter(ParameterSetName = "Path", Mandatory=true, Position=1,

ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)]

[Alias("FullName")]

88

Trang 10

private FileInfo fileInfo = null;

[Parameter(ParameterSetName = "FileInfo", Mandatory = true, Position = 1,

Trang 11

if (File.Exists(path)){

File.SetLastWriteTime(path, date);

}}

}

Please note that the parameterPathis defined to take the value from either the pipeline object or a

property of the pipeline object In the preparation stage of pipeline parameter binding, the two pipeline

parameter lists can be constructed as follows:

❑ ValueFromPipeline List: Path, FileInfo

❑ ValueFromPipelineByPropertyName List: Path

Now consider the following commands:

PS C:\user\gxie> $a = ’c:\user\gxie\readme.txt’

PS C:\user\gxie> $b = get-childitem readme2.txt

PS C:\user\gxie> $c = add-member -InputObject 0 -MemberType NoteProperty -Name P

ath -Value ’c:\user\gxie\readme3.txt’ -passThru

PS C:\user\gxie> $a,$b,$c | touch-file -date 7/1/2007

PS C:\user\gxie> get-childitem

Directory: Microsoft.PowerShell.Core\FileSystem::C:\user\gxie

The first pipeline object is$a, which is a string Because the type of thePathparameter is a string, it will

be bound to take the value of$aduring the step of binding parameters from theValueFromPipelinelist

with no type conversion With this, the timestamp of filec:\user\gxie\readme.txtwill be updated

The second pipeline object is$b, which is of typeFileInfo This matches the type for parameter

FileInfo,FileInfo, so this parameter will take the value of$bduring the step of binding parameters

from theValueFromPipelinelist with no type conversion With this, the timestamp of filec:\user\gxie\

readme2.txtwill be updated

The third pipeline object is$c, which is a wrapped integer with a property namedPath First, an attempt

to bind parameters from theValueFromPipelinelist will fail because neither parameterPathnor

FileInfois of type integer During the step of binding parameters from the

ValueFromPipelineByProp-ertyNamelist (with no type conversion), thePathparameter will be bound to a value of$c.Pathbecause

of type match and name match With this, the timestamp of filec:\user\gxie\readme3.txtwill also be

updated

90

Trang 12

At this point, you know how to make a cmdlet handle command-line input and pipeline input throughcommand parameters In the following sections, we discuss how to show cmdlet execution results to

users This includes cmdlet output and cmdlet execution errors

Generating Pipeline Output

PowerShell cmdlets can write objects to the output pipe by using theWriteObject()method The

fol-lowing example extends theTouch-Filecmdlet to writeFileInfoobjects to the output pipe:

[Cmdlet("Touch", "File", DefaultParameterSetName = "Path")]

public class TouchFileCommand : PSCmdlet

{

private string path = null;

[Parameter(ParameterSetName = "Path", Mandatory=true, Position=1,

ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)]

}set{path = value;

}}

private FileInfo fileInfo = null;

[Parameter(ParameterSetName = "FileInfo", Mandatory = true, Position = 1,

ValueFromPipeline = true)]

public FileInfo FileInfo

{

get{return fileInfo;

}set{fileInfo = value;

}}

DateTime date = DateTime.Now;

[Parameter]

public DateTime Date

Trang 13

get{return date;

}set{date = value;

}}

protected override void ProcessRecord()

fileInfo.LastWriteTime = date;

WriteObject(fileInfo);

}}

Repor ting Errors

Cmdlet execution can encounter exceptions from different sources, including the following:

❑ NET common language runtime (or CLR) or PowerShell — for example, the out of memory

exception from CLR or the pipeline stopped exception from PowerShell

❑ Cmdlet logic itself

❑ Components on which the cmdlet depends

Cmdlets normally don’t need to be concerned about exceptions from the CLR or PowerShell These kinds

of exceptions can be better handled by PowerShell For exceptions from the other two sources, it is the

cmdlet’s responsibility to wrap the exceptions into error records and to report them

92

Trang 14

There are two kinds of errors in PowerShell:

Non-terminating errors:This kind of error is usually specific to the current pipeline object on

which the cmdlet is operating As a result, the cmdlet can skip the current object and move on toprocess the next object from the pipeline

Terminating errors:This kind of error indicates an issue with the cmdlet that prevents it from

handling any pipeline objects For example, theStart-Servicecmdlet depends on the

ser-vice controller for starting a serser-vice If the serser-vice controller is not running, theStart-Service

cmdlet will not be able to start any service As a result, the whole cmdlet needs to be stopped

For normal shells, error handling focuses on reporting errors PowerShell also allows analyzing and

acting upon the errors Usually, errors during PowerShell command execution are accumulated into anarray Then users can analyze the error, fix the problem, and resend the objects not processed through thepipeline To support this capability, PowerShell provides theErrorRecordandErrorDetailclasses

Target object:This is normally the current pipeline object With this, you can determine which

pipeline objects were not successfully processed and need to be processed again

InvocationInfo:This provides context about this error It includes information such as the cmdlet,the pipeline, and which line of a script file was being executed when the error happened

To create anErrorRecordobject, just fill in information about the exception, error category, error ID, andtarget object, as shown in the following example:

[Cmdlet("Touch", "File", DefaultParameterSetName = "Path")]

public class TouchFileCommand : PSCmdlet

{

private string path = null;

[Parameter(ParameterSetName = "Path", Mandatory = true, Position = 1,

ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)]

}set{path = value;

}}

Trang 15

private FileInfo fileInfo = null;

[Parameter(ParameterSetName = "FileInfo", Mandatory = true, Position = 1,

ValueFromPipeline = true)]

public FileInfo FileInfo

{

get{return fileInfo;

}set{fileInfo = value;

}}

DateTime date = DateTime.Now;

[Parameter]

public DateTime Date

{

get{return date;

}set{date = value;

}}

protected override void ProcessRecord()

{

FileInfo myFileInfo = fileInfo;

if (myFileInfo == null && File.Exists(path)){

myFileInfo = new FileInfo(path);

}

if (myFileInfo != null){

try{myFileInfo.LastWriteTime = date;

}catch (UnauthorizedAccessException uae){

ErrorRecord errorRecord = new ErrorRecord(uae,

"UnauthorizedFileAccess",ErrorCategory.PermissionDenied,myFileInfo.FullName);

94

Trang 16

There is no need to provideInvocationInfoduringErrorRecordconstruction Information about thatwill be filled in when the error record is reported

Use the following to run this command:

PS C:\user\gxie> get-childitem readme.txt

Directory: Microsoft.PowerShell.Core\FileSystem::C:\user\gxie

PS C:\user\gxie> get-childitem readme.txt | touch-file

Touch-File : Access to the path ’C:\user\gxie\readme2.txt’ is denied

At line:1 char:17

+ dir | touch-file <<<<

This reported error message is constructed from the exception message andInvocationInfo The

optionalInvocationInfoprovides the line, character, and script block shown at the bottom of this

example error message

ErrorDetails

Frequently, cmdlet developers find that error messages from the exception of the error record are too

general to help users understand and troubleshoot the issue To resolve this, error details can be attached

to error records, as shown in the following example:

[Cmdlet("Touch", "File", DefaultParameterSetName = "Path")]

public class TouchFileCommand : PSCmdlet

{

protected override void ProcessRecord()

Trang 17

FileInfo myFileInfo = fileInfo;

if (myFileInfo == null && File.Exists(path)){

myFileInfo = new FileInfo(path);

}

if (myFileInfo != null){

try{myFileInfo.LastWriteTime = date;

}catch (UnauthorizedAccessException uae){

ErrorRecord errorRecord = new ErrorRecord(uae,

"UnauthorizedFileAccess",ErrorCategory.PermissionDenied,myFileInfo.FullName);

string detailMessage = String.Format("Not able to touch file

’{0}’ Please check whether it is readonly.",myFileInfo.FullName);

errorRecord.ErrorDetails = new ErrorDetails(detailMessage);

WriteError(errorRecord);

return;

}WriteObject(myFileInfo);

}}

}

There are two ways to construct anErrorDetailsobject The simplest way is to directly construct the

object using a message string A more complicated way is to construct the object based on a resource

string and some placeholder arguments To support internationalization, using a resource string is

recommended

Now if you run the command again, you will see that the message from theErrorDetailsobject is

reported from the console:

PS C:\user\gxie> get-childitem readme.txt | touch-file

Touch-File : Not able to touch file ’C:\user\gxie\readme2.txt’ Please check

whether it is readonly

At line:1 char:17

+ dir | touch-file <<<<

96

Ngày đăng: 12/08/2014, 23:21

TỪ KHÓA LIÊN QUAN

w