forked from vexx32/PSKoans
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathAboutLoopsAndPipelines.Koans.ps1
173 lines (152 loc) · 7.33 KB
/
AboutLoopsAndPipelines.Koans.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
using module PSKoans
[Koan(Position = 121)]
param()
<#
The Pipeline & Loops
A core tenet of PowerShell scripts is its pipeline. It's mainly just a fancy way
of iterating over an array of items, so we'll also cover standard loops as well
to give a comparison.
Unlike standard loops, where altering the collection you're iterating through is
considered a terrible idea, so much so that many languages (including PowerShell)
actually throw an error if you try, pipelines are designed to break apart a
collection into its parts and operate on the pieces one at a time.
As such, modifying the 'collection' mid-pipeline is very common. Pipelines are
commonly used to take an array of input and perform multiple actions on each item
before storing or outputting the result.
#>
Describe 'Pipelines and Loops' {
Context 'The Pipeline' {
It 'is used to process input in several stages' {
# The .. operator is used to quickly create arrays with a range of numbers
$Values = 1..5
# Where-Object is a filtering cmdlet, used to discard selected objects mid-pipeline.
$Values | Where-Object { $_ -lt 3 } | Should -Be @(1, 2)
}
It 'often uses $_ or $PSItem to indicate the current item in the pipeline' {
<#
The pipeline takes each element of the array and feeds them one by one
into the next cmdlet.
#>
1..5 |
ForEach-Object {
<#
ForEach-Object is a cmdlet that utilises the pipeline to create a
sort of loop, where each object that it receives from the pipeline
has the same set of actions performed on it.
In pipeline contexts, you will frequently see $_ or $PSItem variables
used. These are automatic variables used to denote 'the current
object in the pipeline'.
#>
$_ | Should -Be $PSItem
<#
Expressions not stored in a variable are dropped back to the output
stream. In pipelines, this means that they are passed along to the
next pipeline command. If a pipeline ends and it is not stored in a
variable, all output will eventually end up in the main output stream.
In the console, this will be displayed on screen.
#>
$_ + 2
} |
Should -Be __ # What happens to the array after we change each value?
}
It 'can store output after processing it through multiple cmdlets' {
<#
Values can be stored at the end of a pipeline by storing the entire
pipeline sequence in a variable. This will create an array of the final
values.
#>
$Strings = 3..7 |
ForEach-Object { "Hello $_!" } | # Line breaks after a pipe character are OK!
Where-Object { $_ -notlike '*5*' } # (Indents are optional.)
__ | Should -Be $Strings
}
It 'can reference previous values from each pipeline segment with -PipelineVariable' {
<#
-PipelineVariable is a common parameter available to all cmdlets and advanced
functions. It takes a single string value, which becomes an extra variable in
the pipeline, similar to $PSItem or $_.
However, rather than referencing the "current" item in the pipeline at that
stage, it instead refers to the "current" item that is output from that
specific pipeline step, allowing a pipeline to self-reference a value from
earlier in the sequence.
#>
1..5 |
Write-Output -PipelineVariable InitialValue |
ForEach-Object { $_ * 2 } |
ForEach-Object { "Initial: $InitialValue, Current: $_" } |
Should -Be @(
"Initial: 1, Current: 2"
"Initial: __, Current: 4"
"Initial: 3, Current: __"
"Initial: __, Current: __"
"Initial: __, Current: __"
)
}
}
Context 'Loop Statements' {
It 'can loop over a collection of items' {
<#
Standard loops are also available in PowerShell, but unlike most other languages we
can still utilise PowerShell's output stream to bundle all their output by assigning
the loop statement to a variable.
#>
$Values = foreach ($Number in 1..5) {
<#
In these kinds of loops, the specified variable name ($Number in this case)
takes the place of the $_ or $PSItem automatic variables instead.
#>
$Number
}
__ | Should -Be $Values
}
It 'can loop a set number of times' {
$Values = for ($i = 0; $i -lt 5; $i++) {
<#
For loops are quite rare in native PowerShell code. They have their uses, but are
frequently too semantically obscure to have a place in PS's verbose ecosystem.
Remember:
1. ++ after the variable will reference the existing value then increment the variable.
2. ++ before the variable will increment the variable and then reference the new value.
#>
$i
}
$Values | Should -Be @(0, 1, 2, 3, 4)
}
It 'can loop while a condition is $true' {
$Values = while ($true) {
<#
Watch out for infinite loops!
Remember: an undeclared variable acts as zero until we increment it!
Incrementing a variable won't output the value
unless the expression is wrapped in parentheses.
#>
(++$Tick)
<#
An alternative would be the following:
$returnvalue = ++$Tick
$returnvalue
Perhaps slightly more clear but more verbose
#>
if ($Tick -gt 2) {
break # the break statement breaks out of the current loop.
}
}
__ | Should -Be $Values
}
It 'can loop one or more times depending on the conditional statement' {
<#
Do..While loops are just like their standard counterparts, but will always
execute the loop at least once:
do { Do-Things } while ($condition -eq $true)
There is also a Do..Until loop, which will run the loop while the condition
evaluates to $false (i.e., until the condition becomes $true):
do { Do-Things } until ($condition -eq $true)
#>
$Values = do {
'eat me!'
# Exactly one iteration occurs despite the condition being $false.
} while ($false)
__ | Should -Be $Values
}
}
}