|
1 | 1 | # Function
|
2 | 2 |
|
3 |
| -## Return |
4 |
| - |
5 |
| -Powershell allows implicit return, and multiple implicit returns. |
6 |
| - |
7 |
| -> [!NOTE] |
8 |
| -> Implicit returns are auto-collected as an array or single value. |
9 |
| -> And it does not print out anything. |
10 |
| -
|
11 |
| -```ps1 |
12 |
| -[int] function Sum { |
13 |
| - param([int]$l, [int]$r) |
14 |
| - $l + $r # implicit return # [!code highlight] |
15 |
| -} |
16 |
| -
|
17 |
| -# You won't need to declare an array and append it on each loop! |
18 |
| -# they're collected automatically as they're implicit returns |
19 |
| -function Foo { |
20 |
| - for($i = 0; $i -lt 10; $i = $i + 1) { |
21 |
| - $i |
22 |
| - } |
23 |
| -} |
24 |
| -
|
25 |
| -(Foo).GetType().Name # object[] # [!code highlight] |
26 |
| -``` |
27 |
| - |
28 |
| -Explicit return is surely supported, but more like a necessity to exit inside a flow. |
29 |
| - |
30 |
| -```ps1 |
31 |
| -[int] function Sum { |
32 |
| - param([int]$l, [int]$r) |
33 |
| - return $l + $r # explicit return # [!code highlight] |
34 |
| - $r + $l # not reachable # [!code warning] |
35 |
| -} |
36 |
| -``` |
37 | 3 |
|
38 | 4 | ## Parameter
|
39 | 5 |
|
@@ -199,11 +165,38 @@ Will throw an error if any parameter does not satisfies the condition.
|
199 | 165 | param (
|
200 | 166 | [ValidateScript({ ($_ % 2) -ne 0 })]
|
201 | 167 | [int[]]$Odd
|
202 |
| - [ValidateScript({ $_.Length < 5 })] |
| 168 | + [ValidateScript({ $_.Length < 5 }, ErrorMessage = "{0} is not valid")] # 0 is the input value |
203 | 169 | [string]$Name
|
204 | 170 | )
|
205 | 171 | ```
|
206 | 172 |
|
| 173 | +- `ValidateLength(min, max)` |
| 174 | +- `ValidateCount(min, max)` |
| 175 | +- `AllowNull()` |
| 176 | +- `AllowEmptyString()` |
| 177 | +- `AllowEmptyCollection()` |
| 178 | +- `ValidatePattern(regex)` |
| 179 | +- `ValidateRange(min, max)` |
| 180 | + - or any value of enum `ValidateRangeKind`: `Positive`, `Negative`, `NonNegative`, `NonPositive` |
| 181 | + ```ps1 |
| 182 | + param( |
| 183 | + [ValidateRange("Positive")] |
| 184 | + [int]$Number |
| 185 | + ) |
| 186 | + ``` |
| 187 | +- `ValidateSet(foo, bar, ...)`: provides completion for predefined entries |
| 188 | +- `ValidateNotNull()` |
| 189 | +- `ValidateNotNullOr()` |
| 190 | +- `ValidateNotNullOrEmpty()`: not a empty string or collection or null |
| 191 | +- `ValidateNotNullOrWhiteSpace()` |
| 192 | +- `ValidateDrive(drive, ...)`: check whether specified path is valid from certain PSDrive |
| 193 | + ```ps1 |
| 194 | + param( |
| 195 | + [ValidateDrive("C", "D")] |
| 196 | + [string]$Path |
| 197 | + ) |
| 198 | + ``` |
| 199 | +
|
207 | 200 | ### Pass By Reference
|
208 | 201 |
|
209 | 202 | Parameter passed by reference is implemented by a wrapper `System.Management.Automation.PSReference`.
|
@@ -279,6 +272,139 @@ function Foo {
|
279 | 272 | }
|
280 | 273 | ```
|
281 | 274 |
|
282 |
| -## Lifetime |
| 275 | +## Mimicking Cmdlet |
| 276 | + |
| 277 | +A function would generally not acting a cmdlet unless it was annotated with `CmdletBinding()` attribute. |
| 278 | + |
| 279 | +`CmdletBinding()` can have the following properties: |
| 280 | + |
| 281 | +- `DefaultParameterSetName`: name of implicit Parameter Set |
| 282 | +- `HelpURI`: link to documenetation |
| 283 | +- `SupportsPaging`: implicitly adds parameters `-First`, `-Skip`, `-IncludeTotalCount`, value accessible by `$PSCmdlet.PagingParameters` |
| 284 | + ```ps1 |
| 285 | + function foo { |
| 286 | + [CmdletBinding(SupportsPaging)] |
| 287 | + param() |
| 288 | + $PSCmdlet.PagingParameters.Skip |
| 289 | + $PSCmdlet.PagingParameters.First |
| 290 | + $PSCmdlet.PagingParameters.IncludeTotalCount |
| 291 | + } |
| 292 | + ``` |
| 293 | +- `SupportsShouldProcess`: implicitly adds `-Confirm` and `-WhatIf` |
| 294 | +- `ConfirmImpact`: specify impact of `-Confirm` |
| 295 | +- `PositionalBinding`: |
| 296 | +
|
| 297 | +<!-- TODO: complete description for ConfirmImpact and PositionalBinding --> |
| 298 | +
|
| 299 | +## Parameter Set |
| 300 | +
|
| 301 | +How a same cmdlet manage different syntax for different usages? The trick is **Parameter Set**. |
| 302 | +Parameter Set is a classification on paramater to distinguish or limit the use of parameters from scenarios. |
| 303 | +
|
| 304 | +- a parameter set must have at least one unique parameter to others to identify the set |
| 305 | +- a parameter can have multiple Parameter Set |
| 306 | +- a parameter can have different roles in different Parameter Set, can be mandatory in one and optional in another |
| 307 | +- a parameter without explicit Parameter Set belongs to all other Parameter Set |
| 308 | +- at least one parameter in the Parameter Set is mandatory |
| 309 | +- only one parameter in set can accept `ValueFromPipeline` |
| 310 | +
|
| 311 | +### Parameter Set Idetifier at Runtime |
| 312 | +
|
| 313 | +`$PSCmdlet.ParameterSetName` reflects the Parameter Set been chosen when a cmdlet is executing with certain syntax. |
| 314 | +
|
| 315 | +
|
| 316 | +## Common Parameters |
| 317 | +
|
| 318 | +Any function or cmdlet applied with `CmdletBinding()` or `Parameter()` attribute has the following implicit parameters added by PowerShell: |
| 319 | +
|
| 320 | +- ErrorAction (ea): specify action on error |
| 321 | +- ErrorVariable (ev): declare **inline** and store the error on the variable instead of `$Error`. Use `-ErrorVariable +var` to append error to the variable |
| 322 | + ```ps1 |
| 323 | + gcm foo -ev bar # inline declaration for $bar |
| 324 | + $bar # contains error |
| 325 | + gcm baz -ev +bar |
| 326 | + $bar # contains two errors |
| 327 | + ``` |
| 328 | + > [!NOTE] |
| 329 | + > The inline variable is an `ArrayList` |
| 330 | +- InformationAction (infa): similar to `ea` |
| 331 | +- InformationVariable (iv): similar to `ev` |
| 332 | +- WarningAction (wa): similar to `ea` |
| 333 | +- WarningVariable (wv): similar to `ev` |
| 334 | +- ProgressAction (proga) |
| 335 | +- OutVariable (ov): declare **inline** and store the output to the variable. Similar to `ev`. |
| 336 | + It's interesting that `-OutVariable` collects incremnentally. |
| 337 | + It collects new item from pipeline on iteration. |
| 338 | + ```ps1 |
| 339 | + 1..5 | % { $_ } -OutVariable foo | % { "I am $foo" } |
| 340 | + # I am 1 |
| 341 | + # I am 1,2 |
| 342 | + # I am 1,2,3 |
| 343 | + # I am 1,2,3,4 |
| 344 | + # I am 1,2,3,4,5 |
| 345 | + ``` |
| 346 | +
|
| 347 | +- Debug (db): print verbose debug message, overrides `$DebugPreference` |
| 348 | +- OutBuffer (ob) |
| 349 | +- PipelineVariable (pv) <!-- TODO: PipelineVariable --> |
| 350 | +- Verbose (vb): whether display the verbose message from `Write-Verbose` |
| 351 | +
|
| 352 | +## Mitigation Parameters |
| 353 | +
|
| 354 | +- WhatIf (wi) |
| 355 | +- Confirm (cf) |
| 356 | +
|
| 357 | +## Return |
| 358 | +
|
| 359 | +Powershell allows implicit return, and multiple implicit returns. |
| 360 | +
|
| 361 | +> [!NOTE] |
| 362 | +> Implicit returns are auto-collected as an array or single value. |
| 363 | +> And it does not print out anything. |
| 364 | +
|
| 365 | +```ps1 |
| 366 | +function Sum { |
| 367 | + param([int]$l, [int]$r) |
| 368 | + $l + $r # implicit return # [!code highlight] |
| 369 | +} |
| 370 | +
|
| 371 | +# You won't need to declare an array and append it on each loop! |
| 372 | +# they're collected automatically as they're implicit returns |
| 373 | +function Foo { |
| 374 | + for($i = 0; $i -lt 10; $i = $i + 1) { |
| 375 | + $i |
| 376 | + } |
| 377 | +} |
| 378 | +
|
| 379 | +(Foo).GetType().Name # object[] # [!code highlight] |
| 380 | +``` |
| 381 | + |
| 382 | +Explicit return is surely supported, but more like a necessity to exit inside a flow. |
283 | 383 |
|
284 |
| -- Function should be define before it was called. |
| 384 | +```ps1 |
| 385 | +function Sum { |
| 386 | + param([int]$l, [int]$r) |
| 387 | + return $l + $r # explicit return # [!code highlight] |
| 388 | + $r + $l # not reachable # [!code warning] |
| 389 | +} |
| 390 | +``` |
| 391 | + |
| 392 | +### Output Type |
| 393 | + |
| 394 | +`OutputTypeAttribute(type: System.Type | System.String, parameterSet?: System.String)` matters when you need auto generated help for the function you write. |
| 395 | +A function can have different return types for different parameter sets. |
| 396 | +PowerShell never do type checking by this attribute, it's just responsible for help generation, write with carefulness! |
| 397 | + |
| 398 | +```ps1 |
| 399 | +[CmdletBinding(DefaultParameterSetName = 'ID')] |
| 400 | +[OutputType('System.Int32', ParameterSetName = 'ID')] |
| 401 | +[OutputType([string], ParameterSetName = 'Name')] |
| 402 | +param ( |
| 403 | + [Parameter(Mandatory = $true, ParameterSetName = 'ID')] |
| 404 | + [int[]] |
| 405 | + $UserID, |
| 406 | + [Parameter(Mandatory = $true, ParameterSetName = 'Name')] |
| 407 | + [string[]] |
| 408 | + $UserName |
| 409 | +) |
| 410 | +``` |
0 commit comments