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

Kumaravel c04.tex V2 - 01/07/2008 11:24am Page 80 Chapter 4: Developing Cmdlets [Parameter(Mandatory=true)] [ValidateEvenNumber] public int Number { get { return number; } set { number = value; } } protected override void ProcessRecord() { } } Now try running the Get-EvenNumber cmdlet with an odd argument value for the Number parameter: PS C: \ user \ gxie > get-evennumber -number 13 Get-EvenNumber : Cannot validate argument on parameter ’Number’. Not an even num Ber. At line:1 char:23 + get-evennumber -number <<<< 13 As you can see, an error is reported because even number validation failed. Parameter Transformation In cmdlet development, parameters can be defined as any .NET types, from simple types such as string , int to complicated types such as System.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 cmdlet Unite-Rectangle , which calculates the union of two rectangles: Using System.Drawing; [Cmdlet("Unite", "Rectangle")] public class UniteRectangleCommand : PSCmdlet { Rectangle rectangle1 = new Rectangle(0,0,0,0); [Parameter(Mandatory = true, Position = 1)] public Rectangle Rectangle1 80 Kumaravel c04.tex V2 - 01/07/2008 11:24am Page 81 Chapter 4: Developing Cmdlets { get { return rectangle1; } set { rectangle1 = value; } } Rectangle rectangle2 = new Rectangle(0, 0, 0, 0); [Parameter(Mandatory = true, Position = 2)] public Rectangle Rectangle2 { get { return rectangle2; } set { rectangle2 = value; } } protected override void ProcessRecord() { WriteObject(Rectangle.Union(rectangle1, rectangle2)); } } You can see that both parameters Rectangle1 and Rectangle2 have the type System.Drawing. Rectangle . To specify rectangle values for these command parameters, you would have to create rect- angle objects first (using the new-object cmdlet) and then pass them to the new cmdlet, as shown in the following 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 PS C: \ user \ gxie > Unite-Rectangle $r1 $r2 Location : {X=1,Y=2} Size : {Width=3, Height=3} X:1 Y:2 Width : 3 Height : 3 Left : 1 81 Kumaravel c04.tex V2 - 01/07/2008 11:24am Page 82 Chapter 4: Developing Cmdlets Top : 2 Right : 4 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 parameter binding. The following code illustrates a custom ListToRectangleConverterAttribute class for converting a list into a rectangle: Using System.Collection; [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public class ListToRectangleConverterAttribute : ArgumentTransformationAttribute { public override object Transform(EngineIntrinsics ei, object inputData) { object input = inputData; if (input is PSObject) input = ((PSObject)input).BaseObject; 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- Attribute class. The bulk of the work for this class is overriding the Tranform method to transform parameter values from one format to another. Inside the Tranform method, transformation is done 82 Kumaravel c04.tex V2 - 01/07/2008 11:24am Page 83 Chapter 4: Developing Cmdlets selectively. More explicitly, you create a new rectangle object only if inputData is a list of four inte- gers. Otherwise, inputData will be passed through as it is. This implementation is chosen so that you don’t mistakenly convert inputData if it is already a Rectangle . In addition, passing through inputData allows another parameter transformation attribute down the chain to also process the data. Now, use this attribute in the Unite-Rectangle cmdlet: [Cmdlet("Unite", "Rectangle")] public class UniteRectangleCommand : PSCmdlet { Rectangle rectangle1 = new Rectangle(0,0,0,0); [Parameter(Mandatory = true, Position = 1)] [ListToRectangleConverter] public Rectangle Rectangle1 { } Rectangle rectangle2 = new Rectangle(0, 0, 0, 0); [Parameter(Mandatory = true, Position = 2)] [ListToRectangleConverter] public Rectangle Rectangle2 { } protected override void ProcessRecord() { WriteObject(Rectangle.Union(rectangle1, rectangle2)); } } Now you can run the new Unite-Rectangle cmdlet by simply passing in two lists: PS C: \ user \ gxie > Unite-Rectangle (1,2,1,1) (3,4,1,1) Location : {X=1,Y=2} Size : {Width=3, Height=3} X:1 Y:2 Width : 3 Height : 3 Left : 1 Top : 2 Right : 4 Bottom : 5 IsEmpty : False 83 Kumaravel c04.tex V2 - 01/07/2008 11:24am Page 84 Chapter 4: Developing Cmdlets In addition, you can verify that directly passing a Rectangle object into either parameter will continue to work: PS C: \ user \ gxie > $r2 = new-object system.drawing.rectangle 3,4,1,1 PS C: \ user \ gxie > Unite-Rectangle (1,2,1,1) $r2 Location : {X=1,Y=2} Size : {Width=3, Height=3} X:1 Y:2 Width : 3 Height : 3 Left : 1 Top : 2 Right : 4 Bottom : 5 IsEmpty : False 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 the touch-file cmdlet: [Cmdlet("Touch", "File", DefaultParameterSetName = "Path")] public class TouchFileCommand : PSCmdlet { [Parameter(ParameterSetName = "FileInfo", Mandatory = true, Position = 1, ValueFromPipeline = true)] public FileInfo FileInfo { get { return fileInfo; } set { fileInfo = value; } } 84 Kumaravel c04.tex V2 - 01/07/2008 11:24am Page 85 Chapter 4: Developing Cmdlets protected override void ProcessRecord() { if (fileInfo != null) { fileInfo.LastWriteTime = date; } if (File.Exists(path)) { File.SetLastWriteTime(path, date); } } } Comparing the preceding code with the example from the ‘‘Parameter Validation’’ section, the only change here is setting the ValueFromPipeline parameter to true for the FileInfo parameter. This informs the PowerShell engine that the parameter FileInfo will 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 Mode LastWriteTime Length Name -a 7/1/2007 12:00 AM 420 readme.txt -a 7/1/2007 12:00 AM 420 readme2.txt In the first command of the preceding example, for each output object from get-childitem*.txt ,the PowerShell engine will bind the Touch-File cmdlet’s FileInfo parameter and call its ProcessRecord() 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 the path parameter 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] 85 Kumaravel c04.tex V2 - 01/07/2008 11:24am Page 86 Chapter 4: Developing Cmdlets 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 Kumaravel c04.tex V2 - 01/07/2008 11:24am Page 87 Chapter 4: Developing Cmdlets In this example, instead of letting the FileInfo parameter take its value from the pipeline, you set TakeValueFromPipelineByPropertyName to be true for the parameter path .Furthermore,youdefine the alias FullName for the parameter path . Now, if the pipeline input object has either a path property or a FullName property, then that property value will be bound to the path parameter. Run the touch-file command 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 Mode LastWriteTime Length Name -a 7/1/2007 12:00 AM 420 readme.txt -a 7/1/2007 12:00 AM 420 readme2.txt You can see that the timestamp of both . txt files are updated. In this case, output of get-childitem *.txt contains a property named FullName (as shown below), which is bound to the path parameter of the touch-file cmdlet: PS C: \ user \ gxie > get-childitem *.txt | get-member -membertype property TypeName: System.IO.FileInfo Name MemberType Definition 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;} Exists Property System.Boolean Exists {get;} Extension Property System.String Extension {get;} FullName Property System.String FullName {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;} Length Property System.Int64 Length {get;} Name Property System.String Name {get;} 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 the BeginProcessing() method of the cmdlet implementation class is called. 87 Kumaravel c04.tex V2 - 01/07/2008 11:24am Page 88 Chapter 4: Developing Cmdlets Conversely, pipeline parameter binding happens once for each pipeline input object. It is performed before each call to the ProcessRecord() 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 it ValueFromPipeline ) is for pipeline parameters taking pipeline input (i.e., the ValueFromPipeline property of the parameter attribute is set to true); another list (let’s call it ValudFromPipelineByPropertyName ) is for pipeline parame- ters taking pipeline input by property name (i.e., ValueFromPipelineByPropertyName is 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 the ValueFromPipeline list with no type conversion.Inthis 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 the ValueFromPipelineByPropertyName list with no type con- version. In this step, if one parameter from the list matches a property’s name of the pipeline input object and the parameter type matches the property type, then this parameter will be bound. Otherwise, parameter binding goes to the next step. c. Bind parameters from the ValueFromPipeline list with type conversion.Inthisstep, if the pipeline input object can be converted into a type of parameter in the list, that parameter will be bound. Otherwise, parameter binding goes to the next step. d. Bind parameters from the ValueFromPipelineByPropertyName list 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 parameter 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 the touch-file cmdlet to specify that both Path and FileInfo take 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 Kumaravel c04.tex V2 - 01/07/2008 11:24am Page 89 Chapter 4: Developing Cmdlets [ValidateNotNullOrEmpty] public string Path { get { return path; } 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 { get { return date; } set { date = value; } } protected override void ProcessRecord() { if (fileInfo != null) { fileInfo.LastWriteTime = date; } 89 [...]... LastWriteTime 7/ 14/ 2007 6 :49 PM 7/1/2007 12:00 AM 7/ 14/ 2007 6 :49 PM Length -42 0 42 0 42 0 Name -readme.txt readme2.txt readme3.txt PS C:\user\gxie> get-childitem | touch-file Mode LastWriteTime Length Name - a 7/ 14/ 2007 6 :49 PM 42 0 readme.txt Touch-File : Access to the path ’C:\user\gxie\readme2.txt’ is denied At line:1 char:17 + dir | touch-file . Microsoft .PowerShell. Core FileSystem::C: user gxie Mode LastWriteTime Length Name -a 7/ 14/ 2007 6 :49 PM 42 0 readme.txt -ar 7/1/2007 12:00 AM 42 0 readme2.txt -a 7/ 14/ 2007 6 :49 PM 42 0 readme3.txt PS. -a 7/ 14/ 2007 6 :49 PM 42 0 readme.txt Touch-File : Access to the path ’C: user gxie readme2.txt’ is denied. At line:1 char:17 + dir | touch-file <<<< -a 7/ 14/ 2007 6 :49 PM 42 0 readme3.txt Because readme2.txt is. "Y"):a Mode LastWriteTime Length Name -a 7/15/2007 5 :46 PM 42 0 readme.txt -a 7/15/2007 5 :46 PM 42 0 readme2.txt -a 7/15/2007 5 :46 PM 42 0 readme3.txt 99

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

TỪ KHÓA LIÊN QUAN

w