Powershell for developers
This is a modal window.
The media could not be loaded, either because the server or network failed or because the format is not supported.
Formal Metadata
Title |
| |
Title of Series | ||
Number of Parts | 150 | |
Author | ||
License | CC Attribution - NonCommercial - ShareAlike 3.0 Unported: You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this | |
Identifiers | 10.5446/51528 (DOI) | |
Publisher | ||
Release Date | ||
Language |
Content Metadata
Subject Area | ||
Genre | ||
Abstract |
|
00:00
Category of beingLengthDirectory servicePhysical systemString (computer science)InformationRevision controlExtension (kinesiology)Programmable read-only memorySet (mathematics)Equals signContent (media)Multiplication signPower (physics)Physical systemComputer fileGastropod shellHand fanGraph coloringType theoryInstance (computer science)Software developerScripting languageObject (grammar)InformationStudent's t-testBitElement (mathematics)Block (periodic table)BuildingPoint (geometry)MereologyMetropolitan area networkINTEGRALRoutingNeuroinformatikProjective planeLevel (video gaming)Error messageLine (geometry)Category of beingProgramming languageNetwork topologyFile systemLibrary catalogDecimalCoefficient of determinationElectronic mailing listSign (mathematics)Dynamical systemComplete metric spaceWindowStandard deviationAreaDirectory serviceBit rateTable (information)2 (number)Right angleSystem callCodeComputer programmingWeightSelectivity (electronic)Content (media)Goodness of fitAnalytic continuationKey (cryptography)ResultantRootXMLUMLComputer animation
09:20
Library catalogParsingSturm's theoremVisual systemApplication service providerFunction (mathematics)Convex hullElectronic meeting systemLine (geometry)Process (computing)Parameter (computer programming)Maxima and minimaHill differential equationContent (media)Scripting languageFunctional (mathematics)Ferry CorstenWritingPRINCE2Statement (computer science)Musical ensembleVideo game consoleSign (mathematics)Instance (computer science)ExpressionParameter (computer programming)ResultantSoftware developerLine (geometry)Computer fileNeuroinformatikObject (grammar)Positional notationThread (computing)Game controllerElectronic mailing listCountingCASE <Informatik>CalculusCodeError messageNumberGroup actionMappingQuicksortElement (mathematics)Block (periodic table)MathematicsOcean currentMeasurementProcess (computing)BuildingRight angleBitContent (media)String (computer science)Java appletFilter <Stochastik>Computer virusWeightVariable (mathematics)Text editorComputer animation
18:37
Line (geometry)Process (computing)Content (media)Parameter (computer programming)Function (mathematics)InformationWindowComputer fileTotal S.A.Variable (mathematics)Computer virusModule (mathematics)Computer fileElement (mathematics)Line (geometry)Functional (mathematics)Rule of inferenceCASE <Informatik>Inheritance (object-oriented programming)Extension (kinesiology)Key (cryptography)Exterior algebraMathematics1 (number)Parameter (computer programming)Scripting languageProcess (computing)Variable (mathematics)Block (periodic table)Video game consoleParsingMetadataQuicksortResultantRevision controlContext awarenessTotal S.A.Different (Kate Ryan album)Source codeGastropod shellPerformance appraisalLibrary (computing)SummierbarkeitForcing (mathematics)System callEqualiser (mathematics)String (computer science)Electronic mailing listAreaPartial derivativeEndliche ModelltheorieType theoryMultiplication signRaw image formatCountingVotingAutomatic differentiationStructural loadPower (physics)Asynchronous Transfer ModeOnline helpWebsiteComputer animation
27:53
Inversion (music)Module (mathematics)Function (mathematics)ForceCore dumpExecution unitSet (mathematics)Directory serviceLengthContinuum hypothesisScripting languageFunctional (mathematics)Directory serviceProgrammer (hardware)BitModule (mathematics)Multiplication signVideo gameCASE <Informatik>Software testingPower (physics)Object (grammar)Software developerExistenceProxy serverWordDirection (geometry)Computer fileSource codeOrder (biology)NumberLibrary (computing)Rule of inferenceArc (geometry)MathematicsEndliche ModelltheorieError messageGastropod shellParameter (computer programming)Product (business)Point (geometry)View (database)Goodness of fitForcing (mathematics)Group actionResultantProgramming languageCodeCuboidComputer animation
37:10
Directory serviceWritingLengthPresentation of a groupError messageHill differential equationDefault (computer science)Control flowScripting languageComputer fileType theoryLine (geometry)Revision controlContext awarenessFerry CorstenCodeScripting languageInformationPhysical systemDatenausgabegerätError messagePoint (geometry)LoginStandard errorParameter (computer programming)WritingMultiplication signBitGraph coloringDirectory servicePosition operatorCodeOcean currentIdentity managementCASE <Informatik>Sound effectBoolean algebraSystem administratorPointer (computer programming)Standard deviationMessage passingFunction (mathematics)WindowGroup actionInteractive televisionVirtual machineDefault (computer science)ArmResultantProper mapSystem callDescriptive statisticsInstance (computer science)State of matterWeightRight angleComputer animation
46:26
Software testingFunction (mathematics)System administratorPrincipal idealInformation securityComputer fileSineScripting languagePhysical systemData typeDirectory serviceComputer fileScripting languageProduct (business)Message passingFunctional (mathematics)Context awarenessMathematicsBlock (periodic table)Ocean currentLambda calculusDisk read-and-write headError messageParameter (computer programming)Goodness of fitPhysical systemBuildingMusical ensembleSound effectDefault (computer science)WritingContent (media)Instance (computer science)Free variables and bound variablesOrder (biology)HypermediaElement (mathematics)WeightAbsolute valueAlgebraic closureBitMultiplication signUniform resource locatorFreewareModule (mathematics)CASE <Informatik>Operator (mathematics)Inheritance (object-oriented programming)PlanningSoftware developerVideo game consoleSoftware bugRight anglePower (physics)Keyboard shortcutLine (geometry)Moment (mathematics)Office suiteComputer animation
55:42
Scripting languageDifferent (Kate Ryan album)Gastropod shellEmailAlgebraic closureBlogAddress spaceTablet computerC sharpInheritance (object-oriented programming)Block (periodic table)TwitterComputer animation
57:10
XMLUML
Transcript: English(auto-generated)
00:04
So, I think it's time. Welcome to this session on PowerShell. My name is Wieder Kongschli, I work here in Oslo. I'm going to talk about PowerShell for developers, but first I would like to tell you a little story.
00:22
Ever since I was a student back in college, I took a liking to the command shells of the Unix systems. So, becoming a big fan, you know, getting used to all the powers of the shell, the programming language, everything.
00:42
I always used to install Cygwin on my Windows computer when I started working, and I was happy with it. But then a few years ago, PowerShell came along, and I didn't really pay much attention to it at first, because I was happy with Cygwin.
01:04
But a couple of years ago, I took the opportunity to learn PowerShell while in a project working on automation and automated builds, continuous integration, stuff like that. I came to find that PowerShell was extremely useful for me as a developer.
01:26
Even though I was not an IT guy, I found it very useful and helpful in my day-to-day work. So, that's some of the pieces that I'm going to talk about today. I'm not a PowerShell expert, I'm first and foremost a PowerShell user, so let's dig into that.
01:53
So, basically this talk has two main topics. First of all, I'm going to talk about PowerShell as a language, looking at some of the features and how we can program with it, how we can build reusable code in PowerShell.
02:06
And the second part of this talk is going to be on how to make good automation scripts using PowerShell. So, first, let's have a look at the PowerShell as a language.
02:26
First of all, opening up the command shell is pretty much similar to a standard Windows user shell. I can list directories, but I can even assign results to a value.
02:43
And then I can have a look, I can have a tab completion, which tells me what to do about the object. And if you're a .NET developer, you kind of recognize some of the things that you can do. I can have a little bit of introspection here, I can have a look at the object that I got here. And on the right here I see that this is basically an array.
03:05
So then I can try and dereference some of the elements. And there's an element, I can get the type of the element, and I can recognize it as a file system info type of object.
03:30
So, if this is an array, one useful thing to do with it is pipe it into an array, use it in a pipeline. I can sort by the property last right time, for instance.
03:47
I can have this sorted, and I can even pipe it further. Select the first one, and I can even have a look at the type of that.
04:06
And I can see that this is actually returning me not an array with one element, but the first element itself. As opposed to if I return two values, then I get an array back. So this is one of the gotchas when you start working with PowerShell, you often have to test whether you have an object or an array.
04:29
So, but I can go on, I can try and have some more information about the object. And then I pipe it to the get member commandlet.
04:42
And there's a huge list of things that you can do. There's properties, there are something called a script property, there are methods. And so on, which tells me what I can do with it. So let's try and make our own object. I can new up a new object.
05:02
And like in .NET, everything inherits from objects, so I have to specify that. And let's see what we got. We got pretty much a standard .NET object back. So, what I then can do is I can add a few members to my object dynamically.
05:24
Add member, let's add a node property, foo with value bar on the object itself. So, if I now have a look, I can see on the bottom line that I have a new property and I can go ahead and call that.
05:48
And that returns bar. Making it a little more interesting, let's do a script property. Then I pass it in a script block with a commandlet that can get the date on the object.
06:10
Let's see what we got. And now I got a script property, let's try and use it.
06:21
And now it returns the current time and if I call this again, we can see that it updates itself. So basically I added a script block to the object. So the takeaway here is that everything is dynamic, I can add properties to my objects. Even though I start with inheriting from a class in .NET, I can just add whatever I like to it afterwards.
06:46
Okay, so what more can we do? We also have basic object types like lists.
07:02
I can create a list with one element. I can add more to the list. I can reference an element like we have actually seen before. List zero, I can reference the last element and so on.
07:25
I also have hash tables, so it prints out the table with all the keys and values. I can dereference, let's say A for instance, and I get the value back.
07:48
So basically also I have a .NET object, so .NET developers will find that they have things from .NET inherited here.
08:02
So that's a little bit about the basic building blocks. One thing to notice about PowerShell is that it comes from another era or point in time than other shells. So one thing that shows that is that it has actually a built-in support for XML, for instance.
08:22
So I have a simple XML file here, which is a catalog of books. So if I try and read that, I tell it to explicitly say that this is going to be XML. Get the content of the books file.
08:43
And what I can do now is I can traverse the XML tree from the command line. So if you remember, the top level of the root element in the document was catalog. So beneath there, there were books, for instance.
09:03
And I get those listed out. I can even reference the first book in the file. Or I can just pass all the books into a pipeline, and then I can filter it over an object.
09:28
And if you filter, you need to apply a script block with the filter itself. So here I write the script block, and the current object in the element gets assigned to the dollar underscore variable.
09:42
So I can say that I want to filter on the genre. So our genre equals computer. I'm only interested in computers. And I can do that sort of filtering. I can then also actually do a mapping, or I can actually change all things on the XML.
10:12
So I can also write this. For all the computer books, I can actually change the genre to, let's say, Star Trek or something.
10:33
And then if I go back and search for computers, there are no computers anymore. Computer books anymore. So XML, I can go ahead then and save my changes.
10:50
And then I can see it's also here that it has been changed in the file itself.
11:04
So those were some of the building blocks that you can use making scripts. So let's have a look at how you kind of group your code. So the first grouping item that I'm going to look into are functions.
11:24
Okay, I'll try and not do that so much. It's just to keep it clean. So let's make a function. I can call that function. Pretty simple.
11:41
I can make it a little bit more interesting. Hello, ARGS. And I can write like so. There is an implicit list of parameters called ARGS. Or I can do an explicit parameter.
12:02
Food, name, hello. And I get the same result, so I can even add more parameters.
12:30
So, like so. If you're a .NET developer or Java developer or JavaScript developer, you might be tempted to write something like this.
12:51
What do you think will happen? Anyone? Let's try it. Something happened.
13:01
To make it a little clearer, I can even do which yields the same result. What happens? Sorry? Yeah, it's an array. Actually, the comma tells us that this is an array.
13:21
So we actually pass in an array into the first formal parameter of the function. So this is a gotcha that you might want to keep notes of. Okay, so those were simple functions, passing parameters into the function.
13:41
Next thing to notice is how we return values from a function. So if I create a new function, bar, I can say something like write host hello from bar,
14:00
and then I return the value 1. So if I call it, it prints hello from bar to the console, and then it prints 1. If I assign this to a variable, we see that it prints hello from bar to the console,
14:23
and my new variable has the value 1. Actually, I don't really need the return keyword because that only tells the thread of control to exit from the function. It doesn't really say anything about the return value, so this will be the same.
14:42
But here's something that's quite unusual. So if I, for instance, write up 2 here, and then I call the function, we see that we get an array out, or this is an array containing 2 and 1.
15:03
And what happens here? Well, the thing is that everything on any statement in the function, if the return value of the statement or the expression is not assigned to anywhere else, it will be passed as a return value from the function.
15:22
So this is something to notice, for instance, if in a function if you call the cmdlet which returns something, and you don't do anything with it, then it will be returned from the function. So to prevent this, I could, for instance, pipe this value to the null device,
15:43
and then it's gone. So this was a very simple function. Now let's move on to a little more advanced function. The case here is that I want to make a function that counts the number of lines in a file.
16:03
So I have a text file which looks something like this. 10 plus lines of code, lines of text, sorry. So let's find a way to count that. So I have to move to the editor.
16:23
So this I'm going to use a more advanced function. Give it a name first. Let's say count lines. And the next thing you see here is a list of parameters to the function. And this is also, there are also annotations to this.
16:44
I'm going to get back to that later on. But there is an annotation saying that this is a mandatory parameter. Let's call it file. And then there are three blocks more. One called begin, one called process, and one called end. We shall see later on what they are useful for.
17:05
But first of all let's try and count the lines of a single file. So I get the content of the file. And then I pass that on to a commandlet called measure object.
17:25
Object, line, lines. And then I just write that as a text string. So then I go on and then I count lines, sorry, text1.txt.
17:49
Let's run it, count. And we get the number 12, which is okay. So what I want to do now, I want to change this code
18:00
so that instead of passing the file as a parameter, I would like to create a pipeline and then pipe it to the function like so. So there is very little I need to do to make this function do this. I only have to annotate it with value from pipeline equals true.
18:26
Okay. What this does is that whatever is in the pipeline, that will be assigned to the file parameter.
18:41
So let's try and run it and see if we get the same result. Yes, we do. And while I'm at it, I would like to pass actually an array of files to count. text2.txt and see what happens. Actually it counts both files exactly what I wanted.
19:06
And this is the key with the process block. The process block will be called for each element in the pipeline and in this case the element will be assigned to the file parameter.
19:22
So let's make it a little more interesting. I would like here to have a list of the file name and also I would like to have the sum of the lines in all of the files. So first of all, let's get the file name.
19:40
File name equals... ... So we have the file name.
20:09
Okay, that's the thing. And now, let's... Ah, okay. So, do you mean the question was what's the difference between the two parameters?
20:25
Well, the dollar parenthesis actually tells the shell to evaluate everything that's inside the parenthesis.
20:41
If I don't do that, then it gets confused with the colon inside the string. So it's basically our parsing problem. So, now we have the file names. Let's count the total.
21:02
And what we do here, the begin block is called before we execute the pipeline and the end block is called after executing on all the elements in the pipeline. So I can create a variable. I can then add to it.
21:23
And then when I'm done, I write the result to the command line, to the console. And there you have it. So these are the kind of internals of the advanced version of function in PowerShell.
21:49
Okay, so now we're done with functions. We have had a look at the parameters, how to pass parameters to it, how to return values from it, and we have shown how to create functions that are useful in pipelines.
22:06
So the next thing I'm going to talk about is scoping. So basically, the scoping rules are pretty simple. There's one global scope and each script file is basically its own scope.
22:24
And inside a function is its own scope. So it's pretty basic. So what I can do now, if I create one file called scope and I define a variable in here, i equals 1, I can create a function f1.
22:52
If I then run the file, I can see that back here in my shell, the variable is gone. It's not there, so it belongs to another scope.
23:03
The same about the function, it's not there. What I can do then is I can explicitly say that it should be defined in the global scope. Like so, and also here. Like so. I can then execute the file and now it's all defined in this scope.
23:26
So I can even remove things from my scope, that's pretty useful. Remove item from functions, remove f1. Now it's gone again. Remove item variable a, and now it's gone.
23:46
So one alternative to using the global scope, which is often a bad idea, you don't want to clutter your global scope so you shouldn't do that. Another way to get these things defined in a file into the parent scope is to so-called dot source the file.
24:11
So what I do then is I prefix the call to the file with dot dot, which basically means that everything that's defined in the file is returned into the current scope.
24:29
So something like, yeah, you have basically the same functionality in all the types of shells, but as you can see now, a is there, the function is there. So this is a very useful thing to do when you are going to make larger scripts that you want to reuse,
24:46
that you want to develop over time, that you create kind of libraries and then you dot source them into your context. However, one shortcoming of this thing is that everything that you define in this file is exported.
25:11
So what if you created sort of helper functions that should only be internal to your script file? Then you cannot do that without sourcing, then everything is available, it's all or nothing.
25:24
So what you can do to help that is to use modules in PowerShell. And modules are pretty simple. There are basically three types of modules. I'm going to look at script modules, which is basically a PowerShell script. There are also binary modules, which is something that you develop in dot net as a DLL and then load,
25:47
and then there is a reference module, which basically adds metadata to other modules. But here, let's make a script module.
26:01
They have the file extension PSM1. So if I define a function, f2, hello from f2, f3, no let's not do that, let's do this first.
26:23
And then I call the cmdlet import module, my module, and then there we are. So what I can do here, if I then create a function, internal function, hello from internal,
26:43
what I need to do then to prevent this one from being exported, then I have another cmdlet that I use to explicitly say what should be exported. So kind of a white list. Export module member, and I am just interested in all the functions that start with an f.
27:07
So if I do that, actually there is something else that we need to take care of here.
27:22
Another feature of the modules are that the modules are cached. So when there are imported ones into the current context, and you call import module again on the same module, they are not loaded if it's already loaded before.
27:43
So this is also important when you develop your module and you make changes to it. You have to either use the force parameter so that it doesn't use the cached one. But in this case, f2 should be exported, but remember the internal function should then not be exported.
28:09
So this way you can create kind of public and private functions inside your module. So you can kind of govern that. And during development you have to remember to use the force
28:26
to prevent your having problems with your changes not being effectuated. So this is also something that I would like to fix.
28:41
So one way I can do that is that I can change the behavior of the cmdlets that I get out of the box. And for that we use something called a proxy function. When PowerShell evaluates your script, all functions take precedence over cmdlets.
29:05
So if you have a function and you have a cmdlet with the same name, it will always choose to call the function. And this one we can leverage in order to change the behavior of what's built in. So let's have a look. Let's do a little bit of introspection first.
29:23
Get command import module. Okay, we didn't get what we wanted. Module name. Okay, so I just want to get the name of the module where the import module cmdlet is defined.
29:47
So I'm going to save that to the clipboard and then I'm creating my new function. New import module ps1.
30:05
I create a function. I call it import module. Then I write a little bit of gibberish just to show that it actually gets called. Use the force loop.
30:21
And then I go on to call the real import. You can see how badly I typed. That's probably the best getaway you get from this class. Okay, import module. And then we explicitly use the force parameter.
30:41
So here again I use the implicit variable args. And basically here I explicitly says that we're going to treat that as an array and pass it into the import module cmdlet.
31:01
And then I can dot source my new module. Let's first remove module my module. And then import module my module. And then you see we get called and my functions are there.
31:23
So then I can do this when I do development of my scripts. I kind of use these proxy functions to change the built-in behavior so that my modules don't get cached. And when I move to production I just remove my proxy function and we're back at the place where we cache the modules.
31:44
Which is good from a performance point of view. Alright, so what we have seen now. We have talked a little bit about the PowerShell language.
32:01
How we can program with it. How we can build our code. We looked at introspection with get type, with get member. It's all objects. It's all .net based objects. We had a brief look at pipelines functions. How to do scoping. We saw that script files and modules could be used to create kind of library, script libraries that we can reuse.
32:30
So the next thing I'm going to talk about is how to make good automation scripts. And the first question there is what's actually a good automation script.
32:41
That's the first thing that we need to define. I've tried to list a few traits that I feel is important. The first trait is indepotent. Basically that means that if we run the script y number of times, n number of times should yield the same result as running it once.
33:07
In other words the script should handle being run several times. And one important thing there in that respect is that if your script consists of steps x, y, z and it failed.
33:28
And it should notice when it runs that step x is already done and then I can move on from y and then do z. So the script needs to handle these kind of situations.
33:43
To show a very simple example, let's do a very simple script. So basically what we want to do, we have three steps here.
34:00
We want to create a directory, we have to empty the directory and then write to a file in that directory. Let's ignore that, it didn't happen. So creating a directory, let's try and do that. Let's make a directory called test.
34:22
It's good, we get a return value or a return object of the new directory. But if I try and do that again, of course I get an error message. So here's an example that the script should handle being run several times. So again, we can use the force to shut it up.
34:45
It doesn't really care whether or not it exists. If it did exist, okay, we are fine, we have the directory, don't worry about it. But that's a special case. Actually I would like to make it explicit in my scripts that it handles both situations.
35:01
If I use the force parameter, as a programmer when I develop my scripts, I don't really know what happened. So I want to make that explicit. So then I can do a test. So if test, let's do not, okay, let's see here.
35:29
Let's try that again. Sublime, steps, ps1, oh it's there, okay.
35:42
Okay, I need a little bit of syntax highlighting. So if the directory doesn't exist, then I make it. So there it's more explicit that I actually have to test or there are actually two paths through my script.
36:02
If it exists, then we do this. If it doesn't exist, we do that. And the next thing, we are emptying the directory. So again I can test. If test, path, test. If there are any files in there, let's delete what's in there.
36:34
Curse, okay. Finally, I just write something to a file in the directory.
36:46
Pretty simple, huh? Let's try and run it. Okay, so we're pretty, we're probably fine. I can try and remove the directory and everything in it and I run.
37:01
And it still runs. So I have a script that can handle both those situations. And the next feature or trait that I find important in script is that they should be extrovert. Basically, they should tell the world what it's doing.
37:22
And it should behave properly so that status messages should go to the standard output device. Error messages should go to the standard error device. So if I go back to my script, I might want to tell a little bit about what I'm doing.
37:41
Write host, create a directory. And I even want to tell if I didn't. What's wrong here? If I didn't create a directory. Write host, directory already exists, and so on.
38:11
Write host, delete all files, or, and so on.
38:29
So it basically tells what it's doing. So in this way, I'm in a better position to know if anything happened or what happened.
38:40
And the other thing, if there is an error situation, there is a commandlet to handle that. Error, the error. And as you can see, we also get a little bit of syntax colorization here. We got some more information about the error, where it occurred, and so on and so forth.
39:03
So basically, if there is an error, use the write error. If there is a status message, use the write host commandlet. So, what's more? The next thing is that when the script has run, I would like to know when it ran, did it fail?
39:25
So basically I want to go back in time and check that it has actually run, what it did, and so on. And PowerShell also has a built-in commandlet that can help us with that. It's called start transcript.
39:40
So there we can pass in which file we want to have our log in. So I can say log.txt. So everything that happens now, from this point on, is actually recorded to the file. So I can list something, and then when I'm done, I can stop transcript and we can have a look inside the file.
40:11
Type log.txt. Let's have a look at the first 20 lines.
40:21
And we can see up here that we have some information that can be very useful to us. Start time, the machine it ran on, which user ran the script, and everything like that, which version of Windows we have, and so on.
40:43
So also in the end, it tells when it stopped. So it added some extra data to the log by itself, and we get everything that was written to the output devices inside the file so that we can see what actually happened. So that's very useful.
41:02
So the next trait that I list is that the script should be a good citizen. So now, what does that mean? And I have a few pointers. The first thing that the script should do is to use proper exit codes. So let's have a look at that.
41:29
Let's make a silly script that actually can fail. Let's add zero here. So basically I call the script. If I tell it to fail, it will exit with exit code one, or otherwise it will have exit code zero.
41:45
So if I want to use that, so what I can do then is that I can call this one. Can fail. And then after the call, I can check on a special parameter.
42:02
This is a boolean which tells whether or not the previous command was successful or not. So in this case it's true. If I go and say do fail, and then I check the result, it's false. We also have another parameter.
42:22
If I do this, there is something called the last exit code. And you can see this is zero. Again, if I make it fail, the last exit code is one. So this I can actually use in my script to do checks.
42:48
So basically after I call, if I call can fail ps1, I basically do the check.
43:06
Did it fail? If it failed, I write an error message and then I exit. I pass on the last exit code to the calling context.
43:20
So now it didn't fail. If I make it fail, then I get passed on the last exit code. So I can check that.
43:40
And this is very good because if I run the script inside a scheduler or in a context where there is no interaction with the user, the exit code is very important to pass on so that the system can detect a failure. One final thing with this that you should take notice of is that you can change the behavior in the script
44:10
when it comes to what the script should do if it encounters an error. So basically there is a global parameter called error action preference.
44:26
And by default, this is set to continue. This basically means that if the script encounters an error, it should continue as if nothing happened. So basically you get an error message and the script goes on.
44:40
So you can also, if you wish, ask it to stop whenever there is an error. Or as you can see here, it can inquire the user about what to do. Not a very good idea when you do automation. So let's not do that. But what will happen then is that it will actually exit on the right error here automatically.
45:10
So if I do right host, still alive, that one will not be printed to the output because it never gots that way.
45:25
It stops when it gets the right error. So the next thing about being a good citizen is trying to always leave the system in a proper state. Even if it fails, the system should be in a well-defined state.
45:46
And one way to achieve that is that I tend to check as much as possible before I try and do steps that have side effects. So basically, for instance, if I need my script or any step in the script needs to run as an administrator,
46:06
I check for that upfront before starting the script. So for instance, here's something that's not very simple,
46:22
but it basically checks the identity of the current user and checks if it's in the administrator role. And this is a nice snippet that I often use to check for exactly that.
46:40
Another good feature in PowerShell in this respect is something called the what-if parameter. So let's say, for instance, I'm curious about what will happen if I try to delete a file. So if I do this, of course I don't know what will happen before I actually do it.
47:04
Or do I? I can just ask the system, what if I run this command, what would happen? And then it will say what it would do. In this case, it will perform the operation, remove the file. So again, I can actually do a check on the return value here to see that it would actually succeed if it actually did run.
47:31
So if there are some steps in my scripts that I'm very curious about or are very important, I check them up front before I move on to actually doing the steps or introducing some kind of effect on the system.
47:52
So again, let's say I delete something that's not really there, what would happen? It, of course, it would send an error message, and I can also see that in my question mark variable here that that wouldn't work.
48:13
So that's a nice trick in that respect.
48:20
So the fifth trait is that my script should be reusable. I wouldn't like to have to rewrite or copy and paste the scripts that I've already created. I would like to reuse them. We have already looked at functions, scripts, modules, how they can be used as
48:40
building blocks to build essentially things that can be reused or used in several contexts. Another thing, this is my personal preference, but when I'm working with file paths, I try to use absolute paths as much as possible.
49:02
So if I have a script that's passed in a relative path, I normally tend to translate that as soon as possible into an absolute path before passing that on to other functions. If I don't do that, my head tends to explode, because if everything is relative
49:23
to the current directory or to the current script file, I tend to get confused. So there's also two commands called pushd and popd that can be useful to change the current directory and then pop the previous one back after you're done.
49:43
So again, that's something to consider making reusable scripts. The next thing is that you should carefully craft the return values of your functions. As we saw earlier on, everything in a function, wherever it is, if it's not
50:03
assigned to any variable, it's going to be passed as a return value from the function. And that can be quite confusing, because normally we are used to that, that's always on the last line, or it's next to the return keyword.
50:23
So here, we cannot be sure of that, so take care about that. We have also looked at the write host and the write error cmdlets. There are two other cmdlets that can come in useful, write debug and write warning.
50:47
So when you're developing your scripts, you should write debug messages using the write debug and not the write host. Here you can switch debug messages or warning messages on and on with a global parameter.
51:04
So during development, you can have debug on and then just switch it off when you're moving into production. But again, you don't have to remove the debug statements, keep them there.
51:21
So, that was basically it, what I had. Are there any questions? Any comments? Does it make sense? We have a little bit of time left, so I'm going to show you something that's pretty different from .NET.
51:52
So what we looked on earlier is that if we have an array, for instance the contents of the directory,
52:05
and pass it into our pipeline, and if we for instance want to filter on them, we passed on a script block. And a script block might look something like a lambda function in C sharp or a delegate.
52:28
But one important change there is that this one does not have any lexical closure in it. So if there are any free variables inside your script block, it will be assigned or it
52:45
will be evaluated in the context where they are executed and not defined, where they are defined. So for instance, if we do the dollar underscore, it will be bound in the context, and the context will be each element in the pipeline.
53:03
So this is also quite useful to know, and you don't have that upfront. So again here, time, we can do something like this. It's evaluated in the current context.
53:29
So what we can do here, actually if we have a script block, we have
53:40
a command on it to get new closure, which will actually explicitly create a closure. So I'm just going to show you a silly demonstration of how this could look.
54:04
So here, let's do this example. Here's the script. It takes one parameter, which is a sound file. Again I translate that to the absolute file name as soon as possible.
54:23
Then I create an instance of the system media sound player, and I set the sound location to the file name, and then I start play. What happens here is that if I run this script, if we disregard this for a moment, if I run this script, it will start playing.
54:44
On my console, I have no way of stopping it, because I need actually the player variable, but that's not defined in my parent scope. So what I can do here is I create a script block, and inside the script block, I ask the player to stop playing.
55:07
But again, if I don't do this, the player is a free variable in the script block, and by default that's evaluating when I call it.
55:25
So in order to create this closure, I call the get closure so that the free variable player here is bound to the player here. So what I can do then, if I run this script, let's save it, script block equals play, and let's find some music.
55:54
I can execute it. So I got the script block back, I can execute it, and the player is actually defined in the parent scope, so I have a closure.
56:22
So that's also a difference in PowerShell from what you're used to in C Sharp. So, okay. Any more questions? Any comments? No, actually, but I know there are a few of those out there, so you can find those.
56:49
So, that's all I had. My handle on Twitter and GitHub is Peter Kongsley. I have a blog where I infrequently blog about PowerShell, and you can contact me at my work email address.
57:05
So, thank you all for coming.