You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/document/Skill/PowerShell/docs/Language/Function.md
+88-27Lines changed: 88 additions & 27 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -11,7 +11,6 @@ function Foo {
11
11
```
12
12
13
13
> [!NOTE]
14
-
>
15
14
> Default type of a parameter is `System.Object`.
16
15
17
16
## Implicit Parameter
@@ -41,30 +40,30 @@ function Foo {
41
40
[string] $Foo,
42
41
[string] $Bar
43
42
)
44
-
43
+
45
44
Write-Output "$Foo $Bar"
46
45
}
47
46
48
47
Foo -Foo foo -Bar bar
49
-
Foo foo bar # it's the same # [!code highlight]
48
+
Foo foo bar # it's the same # [!code highlight]
50
49
```
51
50
52
51
Or use a explicit position argument on attribute.
53
52
54
53
```ps1
55
54
function Foo {
56
55
param (
57
-
[Parameter(Position = 1)] # [!code highlight]
56
+
[Parameter(Position = 1)] # [!code highlight]
58
57
[string] $Bar
59
-
[Parameter(Position = 0)] # [!code highlight]
58
+
[Parameter(Position = 0)] # [!code highlight]
60
59
[string] $Foo,
61
60
)
62
-
61
+
63
62
Write-Output "$Foo $Bar"
64
63
}
65
64
66
65
Foo -Foo foo -Bar bar
67
-
Foo foo bar # it's the same # [!code highlight]
66
+
Foo foo bar # it's the same # [!code highlight]
68
67
```
69
68
70
69
PowerShell starts counting the position when there's a value belonging to no explicit parameter name.
@@ -122,7 +121,7 @@ function Foo {
122
121
```
123
122
124
123
> [!NOTE]
125
-
> For overriding default parameter outside the function, see [$PSDefaultParameterValues](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_parameters_default_values?view=powershell-7.4#long-description)
124
+
> For overriding default parameter outside the function, see [$PSDefaultParameterValues](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_parameters_default_values?view=powershell-7.4#long-description)
126
125
127
126
## Required Parameter
128
127
@@ -139,7 +138,7 @@ param (
139
138
> You can omit assignment for boolean attribute parameter.
140
139
>```ps1
141
140
>param (
142
-
> [Parameter(Mandatory)] # Mandatory is true now # [!code highlight]
141
+
> [Parameter(Mandatory)] # Mandatory is true now # [!code highlight]
143
142
> [string]$RequiredName
144
143
>)
145
144
>```
@@ -151,16 +150,16 @@ Parameters can have aliases. It's not needed for most of time though since pwsh
151
150
```ps1
152
151
function Person {
153
152
param (
154
-
[Alias('n')] # [!code highlight]
153
+
[Alias('n')] # [!code highlight]
155
154
[string]$Name,
156
155
157
-
[Alias('a', 'yearsold')] # can have multiple aliases! # [!code highlight]
156
+
[Alias('a', 'yearsold')] # can have multiple aliases! # [!code highlight]
158
157
[int]$Age
159
158
)
160
159
Write-Host "Name: $Name, Age: $Age"
161
160
}
162
161
163
-
Person -n "Alice" -a 30 # [!code highlight]
162
+
Person -n "Alice" -a 30 # [!code highlight]
164
163
```
165
164
166
165
## Parameter Validation
@@ -204,6 +203,68 @@ param (
204
203
)
205
204
```
206
205
206
+
## Parameter Auto-Conversion
207
+
208
+
For flexibility, PowerShell has no strict type checking on parameter, but it handles implicit conversion on arguments.
209
+
Any argument cannot be converted to the target type specified would cease the execution.
Parameters having **manually** specified value would be recorded into an dictionary `$PSBoundParameters`.
237
+
Such key-value pairs only stores **initially specified** value of those parameters.
238
+
239
+
## Closure & Currying
240
+
241
+
Closure in PowerShell requires a `[scriptblock]` with `GetNewClosure` being called, you can't play it directly with a normal function.
242
+
243
+
As [documentation](https://learn.microsoft.com/en-us/dotnet/api/system.management.automation.scriptblock.getnewclosure?view=powershellsdk-7.4.0) described:
244
+
> `GetNewClosure` returns a new scriptblock bound to a module. **Any local variables in the callers context will be copied into the module**.
245
+
246
+
```ps1
247
+
$bar = 123;
248
+
$closure = {
249
+
param ($foo)
250
+
$bar + $foo
251
+
}.GetNewClosure()
252
+
253
+
& $closure 1 # 124
254
+
```
255
+
256
+
Now we've understand how closure work, we could implement a simple currying like so:
257
+
258
+
```ps1
259
+
function Currying {
260
+
param ($prev)
261
+
{ param($final) $prev + $final }.GetNewClosure()
262
+
}
263
+
264
+
$currying = Currying -prev 1
265
+
$result = & $currying -final 2 # 3
266
+
```
267
+
207
268
## Pass By Reference
208
269
209
270
Parameter passed by reference is implemented by a wrapper `System.Management.Automation.PSReference`.
@@ -212,14 +273,14 @@ Casting to `[ref]` generates a new wrapper containing the value.
212
273
213
274
```ps1
214
275
function Foo {
215
-
param ([ref][int]$foo) # [!code highlight]
276
+
param ([ref][int]$foo) # [!code highlight]
216
277
$foo.Value = 250
217
278
$foo.Value
218
279
}
219
280
220
281
$bar = 1
221
282
Foo ([ref]$bar)
222
-
$bar # 250 # [!code highlight]
283
+
$bar # 250 # [!code highlight]
223
284
```
224
285
225
286
> [!NOTE]
@@ -231,9 +292,9 @@ In a simple function where there's only one series of parameters being taken, we
231
292
But things will explode when we're dealing with a pipeline input which might bring multiple objects.
232
293
233
294
The pipeline mechanism is essentially based on the `Enumerator` so if we collect all items into a new collection as parameter value, it can be a huge performance issue.
234
-
So named blocks are essentially to defined a shared process logic for each object in the pipeline input, and other logic like initializationa and finalization.
295
+
So named blocks are essentially to defined a shared process logic for each object in the pipeline input, and other logic like initialization and finalization.
235
296
236
-
-`begin`: state initializationa for the pipeline iteration.
297
+
-`begin`: state initialization for the pipeline iteration.
237
298
-`process`: logic for each pipeline iteration.
238
299
-`end`: final action for the completed pipeline iteration.
239
300
-`clean`: a `finally` block to execute clean up no matter what happens.(PowerShell 7.3+)
@@ -255,7 +316,7 @@ function Foo {
255
316
> echo hello
256
317
> }
257
318
>}
258
-
># equivalent to
319
+
># equivalent to
259
320
>function Foo {
260
321
> echo hello
261
322
>}
@@ -286,7 +347,7 @@ A function would generally not acting a cmdlet unless it was annotated with `Cmd
286
347
`CmdletBinding()` can have the following properties:
287
348
288
349
-`DefaultParameterSetName`: pwsh will prefer this name when there's a ambiguity between syntax provided.
289
-
-`HelpURI`: link to documenetation
350
+
-`HelpURI`: link to documentation
290
351
-`SupportsPaging`: implicitly adds parameters `-First`, `-Skip`, `-IncludeTotalCount`, value accessible by `$PSCmdlet.PagingParameters`
291
352
```ps1
292
353
function foo {
@@ -299,14 +360,14 @@ A function would generally not acting a cmdlet unless it was annotated with `Cmd
299
360
```
300
361
- `SupportsShouldProcess`: implicitly adds `-Confirm` and `-WhatIf`
301
362
- `ConfirmImpact`: specify impact of `-Confirm`
302
-
- `PositionalBinding`:
363
+
- `PositionalBinding`:
303
364
304
365
<!-- TODO: complete description for ConfirmImpact and PositionalBinding -->
305
366
306
367
## Parameter Set
307
368
308
369
How a same cmdlet manage different syntax for different usages? The trick is **Parameter Set**.
309
-
Parameter Set is a classification on paramater to distinguish or limit the use of parameters from scenarios.
370
+
Parameter Set is a classification on parameter to distinguish or limit the use of parameters from scenarios.
310
371
311
372
- a parameter set must have at least one unique parameter to others to identify the set
312
373
- a parameter can be member of multiple parameter sets.
@@ -315,7 +376,7 @@ Parameter Set is a classification on paramater to distinguish or limit the use o
315
376
- at least one parameter in the Parameter Set is mandatory
316
377
- only one parameter in set can accept `ValueFromPipeline`
317
378
318
-
### Parameter Set Idetifier at Runtime
379
+
### Parameter Set Identifier at Runtime
319
380
320
381
`$PSCmdlet.ParameterSetName` reflects the Parameter Set been chosen when a cmdlet is executing with certain syntax.
321
382
@@ -339,7 +400,7 @@ Any function or cmdlet applied with `CmdletBinding()` or `Parameter()` attribute
339
400
- WarningVariable (wv): similar to `ev`
340
401
- ProgressAction (proga)
341
402
- OutVariable (ov): declare **inline** and store the output to the variable. Similar to `ev`.
342
-
It's interesting that `-OutVariable` collects incremnentally.
403
+
It's interesting that `-OutVariable` collects incrementally.
343
404
It collects new item from pipeline on each iteration.
344
405
```ps1
345
406
1..5 | % { $_ } -OutVariable foo | % { "I am $foo" }
@@ -359,7 +420,7 @@ Any function or cmdlet applied with `CmdletBinding()` or `Parameter()` attribute
359
420
360
421
Mitigation parameters were added when `CmdletBinding(SupportsShouldProcess)` was applied.
361
422
362
-
- WhatIf (wi): shows explaination for the command without executing it.
423
+
- WhatIf (wi): shows explanation for the command without executing it.
363
424
- Confirm (cf): ask for confirmation when executing the command.
> scriptblock created in literal `{}` is always aware of current caller context(SessionState) while `[scriptblock]::Create` returns a **unbound scriptblock** which only reads the SessionState from where it was called.
28
+
> See [this post](https://mdgrs.hashnode.dev/scriptblock-and-sessionstate-in-powershell)
0 commit comments