1
00:00:02,640 --> 00:00:03,710
With all of this,

2
00:00:03,710 --> 00:00:06,430
it's time to go back into PowerShell so I can show you

3
00:00:06,430 --> 00:00:09,440
how to work with objects one at a time.

4
00:00:09,440 --> 00:00:14,080
Hopefully you can take advantage of the PowerShell pipeline and manage at scale,

5
00:00:14,080 --> 00:00:18,340
but when you can't, you'll have to do things like this.

6
00:00:18,340 --> 00:00:18,710
Alright,

7
00:00:18,710 --> 00:00:22,590
I'm going to assume that you're going to read the help for foreach‑object.

8
00:00:22,590 --> 00:00:25,640
Let me just get in and demo it.

9
00:00:25,640 --> 00:00:29,400
Let me begin with at least showing you the demo from the slide,

10
00:00:29,400 --> 00:00:30,780
just so you can see this in action.

11
00:00:30,780 --> 00:00:35,540
So 1..10, remember that dot dot is the range operator,

12
00:00:35,540 --> 00:00:38,430
foreach‑object that comes through the pipeline,

13
00:00:38,430 --> 00:00:43,510
I'm going to multiply that object, in this case the numbers 1 through 10,

14
00:00:43,510 --> 00:00:49,440
by 2, and PowerShell then writes that object to the pipeline.

15
00:00:49,440 --> 00:00:51,790
And just to show you that this is still going on,

16
00:00:51,790 --> 00:00:54,270
this is still PowerShell pipeline stuff,

17
00:00:54,270 --> 00:01:01,940
let's rerun this command, and I'm going to pipe this now to measure‑object.

18
00:01:01,940 --> 00:01:05,040
I don't have to specify a property here,

19
00:01:05,040 --> 00:01:08,580
because the number itself, the object, that is the property,

20
00:01:08,580 --> 00:01:09,610
that is the value.

21
00:01:09,610 --> 00:01:13,400
So if PowerShell does not see a property for measure‑object,

22
00:01:13,400 --> 00:01:17,640
it just assumes that you must be piping in a number,

23
00:01:17,640 --> 00:01:20,600
something that measure‑object can work with.

24
00:01:20,600 --> 00:01:24,310
And so the AllStats will go through, and I can get the count,

25
00:01:24,310 --> 00:01:25,830
and again, the average, the sum,

26
00:01:25,830 --> 00:01:31,840
all the other statistics that measure‑object will provide.

27
00:01:31,840 --> 00:01:34,740
Let's do one other here.

28
00:01:34,740 --> 00:01:36,210
Let's do one more example,

29
00:01:36,210 --> 00:01:38,420
and this is kind of to reinforce something we

30
00:01:38,420 --> 00:01:41,040
looked at in the last demonstration.

31
00:01:41,040 --> 00:01:46,430
So I'm going to do the foreach, and let's measure the object,

32
00:01:46,430 --> 00:01:48,930
get all those statistics, but, you know,

33
00:01:48,930 --> 00:01:53,150
I don't want to see the Property property,

34
00:01:53,150 --> 00:01:58,940
that's blank, just show me the ones that have the numbers there.

35
00:01:58,940 --> 00:02:00,830
So it's much nicer, cleaner output,

36
00:02:00,830 --> 00:02:05,040
we kind of saw that in a previous demonstration.

37
00:02:05,040 --> 00:02:08,040
Let me also show you one other example here,

38
00:02:08,040 --> 00:02:11,050
because you will see this out in the wild,

39
00:02:11,050 --> 00:02:14,040
out, people post this and use this all the time.

40
00:02:14,040 --> 00:02:19,640
That percent symbol that you see there is the alias for foreach‑object.

41
00:02:19,640 --> 00:02:22,690
Personally, I'm not a big fan of that, because if you see that,

42
00:02:22,690 --> 00:02:25,030
as you see that here, and you're new to PowerShell,

43
00:02:25,030 --> 00:02:27,240
you have no idea what that means.

44
00:02:27,240 --> 00:02:28,110
It's cryptic.

45
00:02:28,110 --> 00:02:31,640
You have to go look up, okay, what does percent symbol indicate,

46
00:02:31,640 --> 00:02:35,580
whereas if you see foreach‑object, that's at least a little clearer.

47
00:02:35,580 --> 00:02:37,450
But the result is still the same,

48
00:02:37,450 --> 00:02:40,670
for each number that comes through the pipeline,

49
00:02:40,670 --> 00:02:44,450
divide it by 2, and there you can see the results.

50
00:02:44,450 --> 00:02:47,870
When you're working with PowerShell interactively in the console,

51
00:02:47,870 --> 00:02:50,690
if you want to use the percent symbol, go right ahead.

52
00:02:50,690 --> 00:02:54,110
It's just as easy to start typing foreach and hit Tab

53
00:02:54,110 --> 00:02:58,440
and let PowerShell Tab complete it.

54
00:02:58,440 --> 00:03:02,350
Now let's look at this with real objects other than just number.

55
00:03:02,350 --> 00:03:05,050
And, again, to reinforce the concepts,

56
00:03:05,050 --> 00:03:08,440
we'll start with my generic Get‑Vegetable command.

57
00:03:08,440 --> 00:03:12,330
So we do Get‑Vegetable, and I'm going to look at get‑member here,

58
00:03:12,330 --> 00:03:16,270
because there is a method here called Peel.

59
00:03:16,270 --> 00:03:19,270
The Peel method has no parameters.

60
00:03:19,270 --> 00:03:24,640
That's why you see just the parentheses with nothing inside.

61
00:03:24,640 --> 00:03:30,390
If I look at my output for this cmdlet and look at the objects,

62
00:03:30,390 --> 00:03:34,700
there is a property that indicates whether something is peeled or not.

63
00:03:34,700 --> 00:03:37,850
So right now you can see nothing is peeled.

64
00:03:37,850 --> 00:03:42,410
There are no commands that will allow me to peel or to invoke that method,

65
00:03:42,410 --> 00:03:48,240
so if I want to use that method, I have to do it individually.

66
00:03:48,240 --> 00:03:51,110
So I'll just grab, say, the Carrot vegetables.

67
00:03:51,110 --> 00:03:55,590
For each Carrot object that comes through the pipeline,

68
00:03:55,590 --> 00:03:58,950
I'm going to invoke the peel method.

69
00:03:58,950 --> 00:04:04,410
So $_ is the current object, and then .peel,

70
00:04:04,410 --> 00:04:05,330
and then in parentheses,

71
00:04:05,330 --> 00:04:07,830
you have to include the parentheses for the method even

72
00:04:07,830 --> 00:04:11,740
if it doesn't have any parameters.

73
00:04:11,740 --> 00:04:14,600
Now, that method doesn't write anything to the pipeline,

74
00:04:14,600 --> 00:04:21,020
so I don't see a result, but we can easily verify that.

75
00:04:21,020 --> 00:04:25,140
Let's just get all the C vegetables, and again,

76
00:04:25,140 --> 00:04:28,940
just select a few properties like the Name and IsPeeled.

77
00:04:28,940 --> 00:04:32,580
So there we go, so Carrot, you can see there actually IsPeeled,

78
00:04:32,580 --> 00:04:35,440
so I know that that method did its thing.

79
00:04:35,440 --> 00:04:46,240
If I wanted to use that method again, I had to invoke it or use it one at a time.

80
00:04:46,240 --> 00:04:48,050
Let's look at another example here.

81
00:04:48,050 --> 00:04:51,140
I'm going to change to my work folder here,

82
00:04:51,140 --> 00:04:57,640
and I'm going to create two test files, and let's do test.txt,

83
00:04:57,640 --> 00:05:01,090
and then I'm just going to, again, create text2.txt.

84
00:05:01,090 --> 00:05:05,140
All I'm doing is running get‑date and sending the output instead

85
00:05:05,140 --> 00:05:07,740
of to the pipeline and showing on my screen,

86
00:05:07,740 --> 00:05:11,940
I'm sending it to the text file.

87
00:05:11,940 --> 00:05:17,640
Now if I look at my files here, and select the name and attributes properties,

88
00:05:17,640 --> 00:05:19,840
you can see the results there.

89
00:05:19,840 --> 00:05:20,120
Now,

90
00:05:20,120 --> 00:05:25,100
I happen to know that if I look at a particular file

91
00:05:25,100 --> 00:05:31,540
like test.txt and look at get‑member, that there is a method called Encrypt.

92
00:05:31,540 --> 00:05:34,760
So I want to encrypt each of the files, or not all the files,

93
00:05:34,760 --> 00:05:38,440
I want to encrypt my test files that I just created.

94
00:05:38,440 --> 00:05:43,040
There's no cmdlet out of the box that will do that for me.

95
00:05:43,040 --> 00:05:44,750
I can use that method, but again,

96
00:05:44,750 --> 00:05:51,240
I have to invoke it or run it for each object that comes through the pipeline.

97
00:05:51,240 --> 00:05:55,570
So let's do a listing of my test.txt files.

98
00:05:55,570 --> 00:06:01,860
For each object, I'm going to invoke or run that encrypt method,

99
00:06:01,860 --> 00:06:04,240
and again, it's all done.

100
00:06:04,240 --> 00:06:07,740
The method doesn't write anything to the pipelines, so I don't see a result.

101
00:06:07,740 --> 00:06:09,170
But I can verify the results,

102
00:06:09,170 --> 00:06:13,400
let's rerun the directory listing and select the name and attributes,

103
00:06:13,400 --> 00:06:17,330
and now you can see that those files actually are encrypted,

104
00:06:17,330 --> 00:06:20,140
so that did work.

105
00:06:20,140 --> 00:06:23,790
Sometimes you want to take advantage of the foreach,

106
00:06:23,790 --> 00:06:28,390
because there is no other way to do something using the PowerShell pipeline.

107
00:06:28,390 --> 00:06:30,750
Now what I'm about to show you is not necessarily the

108
00:06:30,750 --> 00:06:34,660
only way to accomplish this task, but it's a good illustration,

109
00:06:34,660 --> 00:06:37,240
I think, of the concepts here.

110
00:06:37,240 --> 00:06:42,140
So if you look at help, for Get‑Volume, there is a parameter called cimsession,

111
00:06:42,140 --> 00:06:45,540
which, in essence, is a remote computer name.

112
00:06:45,540 --> 00:06:49,400
Don't worry too much about cimsessions and remoting,

113
00:06:49,400 --> 00:06:51,800
because you may not have gotten into that yet.

114
00:06:51,800 --> 00:06:55,990
If I give a computer name a value to cimsession,

115
00:06:55,990 --> 00:07:00,640
it will connect to that computer and get me the volume information.

116
00:07:00,640 --> 00:07:04,940
I have a text file in my scripts folder called servers.txt,

117
00:07:04,940 --> 00:07:09,240
and you can see the servers that are listed there.

118
00:07:09,240 --> 00:07:15,130
I'd like to be able to pipe this list to Get‑Volume and get the

119
00:07:15,130 --> 00:07:17,830
C drive information for all those servers,

120
00:07:17,830 --> 00:07:25,140
so I might try something like this, get‑content and then pipe it to Get‑Volume.

121
00:07:25,140 --> 00:07:30,140
And that fails, and the error message tells you that it fails.

122
00:07:30,140 --> 00:07:31,440
Scroll back up here.

123
00:07:31,440 --> 00:07:33,980
If you looked at the help output,

124
00:07:33,980 --> 00:07:39,560
you can see that this parameter does not accept pipeline input,

125
00:07:39,560 --> 00:07:45,740
so in order to give it a value, I'm going to have to do it one at a time.

126
00:07:45,740 --> 00:07:46,860
Technically,

127
00:07:46,860 --> 00:07:51,350
I could do multiple values at the same time because see that

128
00:07:51,350 --> 00:07:54,630
cimsession and the little double square brackets,

129
00:07:54,630 --> 00:08:00,230
I could do Get‑Volume ‑cimsession server 1,

130
00:08:00,230 --> 00:08:02,200
server 2, server 3, all separated by commas,

131
00:08:02,200 --> 00:08:02,670
but, you know,

132
00:08:02,670 --> 00:08:08,510
I want to take advantage of the pipeline. So I can do something like this.

133
00:08:08,510 --> 00:08:12,220
So let's get my list of servers and the whole

134
00:08:12,220 --> 00:08:14,410
expression I'm going to save to a variable,

135
00:08:14,410 --> 00:08:17,040
$c.

136
00:08:17,040 --> 00:08:19,970
For each server name that comes through that list,

137
00:08:19,970 --> 00:08:23,370
I'm going to assign it the value to cimsession,

138
00:08:23,370 --> 00:08:28,140
to that parameter, and we're going to get Drive C.

139
00:08:28,140 --> 00:08:34,440
PowerShell will run for a moment and saves the results to $c.

140
00:08:34,440 --> 00:08:37,710
Pretty cool, so that worked, I was able to tell PowerShell,

141
00:08:37,710 --> 00:08:40,740
get the list of computers one at a time,

142
00:08:40,740 --> 00:08:45,760
call Get‑Volume and pass that computer name and feed

143
00:08:45,760 --> 00:08:49,640
that into the cimsession parameter.

144
00:08:49,640 --> 00:08:50,460
But, you know, here,

145
00:08:50,460 --> 00:08:53,600
let's continue our little exploration here with

146
00:08:53,600 --> 00:08:56,700
these cmdlets to work with objects.

147
00:08:56,700 --> 00:09:02,540
Let's take that variable and let's pipe to Select object,

148
00:09:02,540 --> 00:09:04,830
and let me select a few properties.

149
00:09:04,830 --> 00:09:08,360
Now I know from piping this to get‑member, I know what the property names are.

150
00:09:08,360 --> 00:09:11,030
So I want to see the computer name because I can't tell that

151
00:09:11,030 --> 00:09:14,040
from the output that I see there from $c,

152
00:09:14,040 --> 00:09:17,860
the DriveLetter and any parameter that starts with Size,

153
00:09:17,860 --> 00:09:20,840
and once again, I'm using the wild card there.

154
00:09:20,840 --> 00:09:23,050
Cool, that's a much easier output.

155
00:09:23,050 --> 00:09:27,250
Now I can see the Size and SizeRemaining of each of

156
00:09:27,250 --> 00:09:29,940
the servers that were in my list.

157
00:09:29,940 --> 00:09:33,900
Eventually you'll learn some other tricks to format those numbers into

158
00:09:33,900 --> 00:09:38,250
megabytes or gigabytes or something a little more useful to you,

159
00:09:38,250 --> 00:09:45,940
but for now, PowerShell selected what I needed and presented it on the screen.

160
00:09:45,940 --> 00:09:51,740
How about a more complete example of all of this?

161
00:09:51,740 --> 00:09:54,650
Let's try a directory listing of windows and get all the

162
00:09:54,650 --> 00:09:59,940
files in it, and let's send this to group object and group

163
00:09:59,940 --> 00:10:03,740
on the extension property.

164
00:10:03,740 --> 00:10:07,060
Then we'll select some properties, we'll do the Name,

165
00:10:07,060 --> 00:10:10,380
Count, and now I'm going to do something slightly advanced.

166
00:10:10,380 --> 00:10:14,820
You'll see examples of this in the help for select object.

167
00:10:14,820 --> 00:10:17,540
I'm going to create a custom property on the fly,

168
00:10:17,540 --> 00:10:24,640
don't worry too much if you don't fully understand it at this point.

169
00:10:24,640 --> 00:10:27,610
I'm going to create a property called Size,

170
00:10:27,610 --> 00:10:31,190
and the value of Size is going to be the results of this

171
00:10:31,190 --> 00:10:33,490
Expression script block that I'm doing.

172
00:10:33,490 --> 00:10:37,480
And I'm going to take the group object, remember,

173
00:10:37,480 --> 00:10:40,840
$_ means the current object in the pipeline.

174
00:10:40,840 --> 00:10:43,640
So I have a group object that I'm processing.

175
00:10:43,640 --> 00:10:43,960
Remember,

176
00:10:43,960 --> 00:10:48,420
the group property is a list of all of the related files,

177
00:10:48,420 --> 00:10:51,240
in this case, grouped by extension.

178
00:10:51,240 --> 00:10:54,780
So I'm going to take all of those files and pipe them to Measure

179
00:10:54,780 --> 00:10:58,080
object and measure the length and get the sum.

180
00:10:58,080 --> 00:11:00,270
Now all I want is just that sum value,

181
00:11:00,270 --> 00:11:05,550
so one way to do that is to pipe this to select object and tell

182
00:11:05,550 --> 00:11:11,420
it to use expandproperty on that sum property.

183
00:11:11,420 --> 00:11:13,090
That'll just give me the value,

184
00:11:13,090 --> 00:11:16,930
it won't give me an object with a single property of sum.

185
00:11:16,930 --> 00:11:23,340
That value then gets assigned to the Size property that I'm creating on the fly.

186
00:11:23,340 --> 00:11:26,440
So I got a new object going through the pipeline.

187
00:11:26,440 --> 00:11:31,160
I can sort on that new property that I created in descending

188
00:11:31,160 --> 00:11:37,540
order and then Select just the first 5.

189
00:11:37,540 --> 00:11:43,240
So now I can see in my windows folder these are the file extensions that are

190
00:11:43,240 --> 00:11:47,410
using the most space or have the the largest number of files,

191
00:11:47,410 --> 00:11:48,640
and, not surprisingly,

192
00:11:48,640 --> 00:11:54,940
the executable files in C windows are taking up the most space.

193
00:11:54,940 --> 00:11:59,410
There are some alternate ways of using foreach‑object,

194
00:11:59,410 --> 00:12:00,820
which I at least need to show you,

195
00:12:00,820 --> 00:12:03,970
even though I don't necessarily recommend using them,

196
00:12:03,970 --> 00:12:07,440
and I don't use them really myself, but let me show you.

197
00:12:07,440 --> 00:12:12,070
So let's go to my scripts folder, and I'm just going to create,

198
00:12:12,070 --> 00:12:15,990
this is a good example of using that range operator,

199
00:12:15,990 --> 00:12:18,310
and I'm going to create test files.

200
00:12:18,310 --> 00:12:21,040
So for each number that comes in,

201
00:12:21,040 --> 00:12:25,670
I'm going to take Get‑Date and pipe it to Out‑File and create a

202
00:12:25,670 --> 00:12:28,830
file called secret‑ whatever the number is,

203
00:12:28,830 --> 00:12:32,510
secret‑12345.dat.

204
00:12:32,510 --> 00:12:37,640
The contents would just be the date.

205
00:12:37,640 --> 00:12:41,330
And if I take a look at the list, so I just created those files.

206
00:12:41,330 --> 00:12:45,470
Now if I wanted to modify, say I want to hide those files,

207
00:12:45,470 --> 00:12:52,340
I need to adjust and modify the attributes property of those files.

208
00:12:52,340 --> 00:12:53,780
One way to do it is like this.

209
00:12:53,780 --> 00:12:57,790
I can get my directory listing of those files,

210
00:12:57,790 --> 00:13:04,310
pipe them to foreach, instead of using $_,

211
00:13:04,310 --> 00:13:07,460
technically you're allowed to use something called

212
00:13:07,460 --> 00:13:12,040
psitem. $psitem is the same thing as $_,

213
00:13:12,040 --> 00:13:14,930
it means the current object in the pipeline,

214
00:13:14,930 --> 00:13:19,640
and I'm just telling it add Hidden to the attributes property.

215
00:13:19,640 --> 00:13:27,040
The += sign is a special operator that says add it to an existing collection.

216
00:13:27,040 --> 00:13:27,830
Now that they're hidden,

217
00:13:27,830 --> 00:13:31,740
if I want to see them actually I have to use the ‑force parameter,

218
00:13:31,740 --> 00:13:36,040
and in the output there you can see that they are in fact hidden.

219
00:13:36,040 --> 00:13:37,980
I'm not a big fan of $psitem.

220
00:13:37,980 --> 00:13:42,300
For one, not a lot of people have used it,

221
00:13:42,300 --> 00:13:44,590
and I think it's just kind of a little more confusing,

222
00:13:44,590 --> 00:13:45,760
personally, myself.

223
00:13:45,760 --> 00:13:51,240
I think you should be able to do $_, so in other words, do this.

224
00:13:51,240 --> 00:13:53,970
So I get all my files again that are hidden,

225
00:13:53,970 --> 00:13:57,160
and in this case I'm going to undo the operation using

226
00:13:57,160 --> 00:14:02,690
$_ and remove that Hidden attributes, and now I've unhidden those files.

227
00:14:02,690 --> 00:14:08,510
I think that's a much cleaner approach, it's more consistent, but pick one.

228
00:14:08,510 --> 00:14:12,190
If you happen to like $psitem, then use that all the time.

229
00:14:12,190 --> 00:14:13,340
The worst thing to do, though,

230
00:14:13,340 --> 00:14:17,470
is to go back and forth and have some code using $_ and some

231
00:14:17,470 --> 00:14:23,340
using $psitem. Pick one and go with that.

232
00:14:23,340 --> 00:14:28,340
And there you can see that the files are in fact now unhidden.

233
00:14:28,340 --> 00:14:31,820
One thing I want to point out, though, with foreach‑object,

234
00:14:31,820 --> 00:14:36,480
do not, I mean it'll work, because I'm going to show you here that it will work,

235
00:14:36,480 --> 00:14:38,340
don't do this.

236
00:14:38,340 --> 00:14:40,940
Don't say, hey, I want to get a list of the files,

237
00:14:40,940 --> 00:14:44,840
but for each one, just show me the name property.

238
00:14:44,840 --> 00:14:45,860
Don't do that.

239
00:14:45,860 --> 00:14:48,460
This is what we used to do with VBScript and with

240
00:14:48,460 --> 00:14:51,220
other kind of old legacy‑style tools.

241
00:14:51,220 --> 00:14:52,970
This is where you're thinking like text.

242
00:14:52,970 --> 00:14:56,940
You're saying, give me this thing, but just show me the text.

243
00:14:56,940 --> 00:15:00,770
You don't have to do that one at a time, even though, yes, I know it works.

244
00:15:00,770 --> 00:15:04,920
When I see something like this, or if you see something like this,

245
00:15:04,920 --> 00:15:08,140
that should be your clue, okay, this person does not fully,

246
00:15:08,140 --> 00:15:14,240
they haven't gotten their head around this objects‑in‑the‑pipeline paradigm.

247
00:15:14,240 --> 00:15:16,550
The PowerShell way to do it would be this.

248
00:15:16,550 --> 00:15:20,300
If all I want to see is the file name, then just tell PowerShell,

249
00:15:20,300 --> 00:15:24,340
get me these file objects, and then just select the name.

250
00:15:24,340 --> 00:15:26,710
In this particular case, what, remember, our PowerShell is doing,

251
00:15:26,710 --> 00:15:32,740
it's creating a custom object, in this case with a single property called Name.

252
00:15:32,740 --> 00:15:38,410
If you truly just want the values, say, for a text list,

253
00:15:38,410 --> 00:15:41,810
you can tell PowerShell to expand that property,

254
00:15:41,810 --> 00:15:46,740
so we'll just select object, expand that Name property.

255
00:15:46,740 --> 00:15:48,720
Now I don't have that little name header,

256
00:15:48,720 --> 00:15:50,690
that's my clue that this is not an object,

257
00:15:50,690 --> 00:15:54,640
it's just a list of names.

258
00:15:54,640 --> 00:15:57,760
And, in fact, once you get more proficient in PowerShell,

259
00:15:57,760 --> 00:16:02,540
you can even shortcut all of that and just use syntax like this,

260
00:16:02,540 --> 00:16:04,490
gives me the same results.

261
00:16:04,490 --> 00:16:08,510
So the command inside the parentheses runs first,

262
00:16:08,510 --> 00:16:12,710
and then I can use the .name property and get the name

263
00:16:12,710 --> 00:16:14,510
property of all of those objects.

264
00:16:14,510 --> 00:16:18,240
So implicitly PowerShell is kind of doing a foreach object,

265
00:16:18,240 --> 00:16:25,000
don't worry, you don't have to get that way right now, but I wanted to point that out to you.

