Andy Schneider's Blog on Windows PowerShell and other random thoughts

/\/\o\/\/ and Joel Bennet both chimed in and provided a much more elegant solution, just override out-default. This is really what I had wanted to do, but didn't know how. Thanks to both /\/\o\/\/ and Joel for your input. Please do check out their comments on this post. But here is the code they provided:

1:# From /\/\o\/\/

2:

3:function out-default {

4: $input | Tee-Object -var global:lastobject |

5: Microsoft.PowerShell.Utility\out-default

6: }

7:

8:# And from Joel

9:# In case you are using custom formatting

10:# You will need to override the format-* cmdlets and then

11:# add this to your prompt function

12:

13:if($LastFormat){$LastOut=$LastFormat; $LastFormat=$Null }

14:

15:

16:

A couple of days ago, an intern that is working for us, was helping me with a Powershell script to manage our Hyper V Cluster. The script ran fine but we were querying 7 different computers and then rolling up all the output into a custom object, so the thing took a while to run. It was just long enough to be annoying. During this process, he asked if there was a way to have PowerShell automatically store the output of the last command in a variable automatically.

I first went down the road of using Tee-object, According to the built-in help,

The Tee-Object cmdlet send the output of a command in two directions (like the letter T). It stores the output in a file or variable, and also sends it down the pipeline. If Tee-Object is the last command in the pipeline, the command output is displayed in the console.

But of course, my Intern (rightfully so) declares this to be unsatisfactory. He wants it to just happen automagically. Picky intern, huh?

Then on the bus ride home this afternoon, I was thinking about Jeffrey's post on Push-Noun. He basically shows us how to set up a loop that goes forever, taking input and executing it only for a specific noun in Powershell.

Considering this, I realized I could do something similar for my issue. So here's the code, stolen from Push-Noun.

This isn't exactly bulletproof. I think I would like to have an automatic variable that stores the output of the last command that was executed, just in case you want to mess with it and you don't want to have to run it again. Basically just override out-host to use a Tee-Object -variable lastcommandoutput or something along those lines.

A meme consists of any unit of cultural information, such as a practice or idea, that gets transmitted verbally or by repeated action from one mind to another.

So here it goes:

How old were you when you started using computers?

11

What was your first machine?

The first machine I got to use was an Apple IIE. Later on, my family purchased a Mac IIsi.

What was the first real script you wrote?

This really isn't scripting, but my first introduction to any type of programming was on a Mac in Junior High School where we learned to use Hyper Card. It was basically a computerized flip book, and I had a guy walk across the screen and open a door.

The first real script was a batch file that ran a bunch of installers silently for a workstation build for our EE building's computer lab in college.

What languages have you used?

Powershell

VBScript

Ruby

C# (I'm calling this scripting since you can use inline C# with the new Add-Type Cmdlet in the Powershell V2 CTP2 :) )

What was your first professional Sysadmin gig?

I worked for a small consulting firm called Starr Technologies and we did IT for several Venture Capital firms in Menlo Park, CA.

If you knew then what you know now, would you have started in IT?

Absolutely! I studied Electrical Engineering in college, but transistors, resistors, and capacitors just never really did it for me. My senior year I took a class on Internet communications and haven't turned back sense. Looking back though, I wish I had taken more CS classes though.]

If there is one thing you learned along the way that you would tell new Sysadmins, what would it be?

Two things.

Get involved in the community. This is something I am learning about now. The Powershell community is really the first "online" community I have actively participated in and I am loving it. It is so much easier to learn with people who are passionate about the same things as you are.

Second is find a great mentor whom you can trust. This probably is not your manager, and may not be someone you work with directly. Buy him/her lunch. People rarely turn down free food, even if they are crazy busy. If people like what they do, they would love the opportunity to teach people. I have a mentor for my professional career as well as for my personal life. I also love to teach younger people as well. It's a two way street: always teach, and always be willing to be taught.

What is the most fun you have had scripting?

The best part of scripting is when someone asks you if you can do x, y, or z and you come back to them and say, "Hey, I wrote this little script for you." I love watching their jaw drop after I show them the one or two lines of Powershell I used to make it happen.

I have been using CTP2 on my computers for sometime and I recently came across a situation where I needed a function that would easily accept parameters from the pipeline and also as a standard parameter. This was the perfect excuse to start playing with Script Cmdlets. These things have all kinds of cool attributes that you can use to make your scripts easier to use, which is all wonderfulness.

So I cracked open Powershell and started playing. It took me a little while but I figured out how to convert my function over to a ScriptCmdlet.

But there was a problem. I had to read lots of documentation to figure it out. What I love about Powershell is how it is so discoverable with use of get-member.

Buried in Powershell is something called a CommandInfo object. This object describes a command and what it can do. Wouldn't it be cool if these objects had information about the parameters, whether or not they accepted values from the pipeline, and what position they were in. The list of options goes on and on.

I think the best way to explain is to show some code that could exist.

1: # Build Up A parameter object

2: $param.Name = "File"

3: $param.HelpMessage = "Please Enter a file name"

4: $param.acceptsValueFromPipeline = $TRUE

5:

6: # Build a block that will go into the processBlock of the ScriptCmdlet

At the very end, the $cmdlet.write() method would write the code for you.

Well, now here comes the fun part (With warnings)

This code was written hacked together and is absolutely not guaranteed to work and I cannot be held liable if it eats your cat or kills your computer. The purpose here is to really see if there would be any interest in furthering this. Eventually i would like to see these properties get added to the System.Management.Automation.FunctionInfo class, or create a new class that inherits from FunctionInfo.

But with commands like ipconfig or netsh that are not native to Powershell, just tacking on the computername property is a little difficult. When you pipe everthing to select-object, format-table, or format-list, which properties do you select.

Under normal circumstances, with say something like Get-Process, you could pipe it to Format-Table Id, Name, WorkingSet.

But with native commands, there are no properties. So here's what you can do.

You can pipe it to select-object and select the whole object using $_ and then also select $_.ComputerName. The only trick is you have to pass the properties in as scriptblocks,

I have found on occasion the need to ping a list of computers, whether they be in a text file, an array in Powershell, or a CSV file. It turns out building commands as a string and then executing that string as a command in Powershell is not exactly intuitive.

You need to use invoke-expression -command $cmd where $cmd is the string you want to execute.