"Hello, World!" 

That's how you write "Hello, World" in PowerShell.  No, this isn't a flashback to PowerShell for N00bs 1.  Back then, we skimmed over some interesting concepts.  Specifically:

  • PowerShell is Object Oriented.
  • "Hello, World" (and every object) has a ToString() method.

Object Oriented isn't very obvious to fuctional / procedural scripters (like us).  We're more used to .BAT and .CMD files where data is simply data, and to get information about that data, such as "How long is this string?" or "Where's the first occurence of the letter 'a' in that string?" is implemented by stand-alone functions that expect a string as input and will return an interger.

An object, as we said before, encapsulates:

  • data
  • properties of that data
  • fucntionality

Rather than turn this into a theoretical discussion of what is or is not OO, let's just use it and see if that helps us grok it. 

PSH> $aString = "Hello, World!"
PSH> $aString.ToString()
Hello, World!

We asserted earlier that every object has a ToString() method.  A 'method' is just a function that's created with the chunk of data, er, object.  To shove a string through its ToString() method isn't a big deal: we get out what we put in.  Let's try something we know will generate different output than input.

PSH> $aString.Length
13

That's a little more interesting.  We might be interested only in lines longer than 80 characters, for example.  Or we may want to use the line length as an index for SubString()... Oops, getting ahead of ourselves here.  The Length member doesn't have (parenthesis).  It doesn't do anything to the data - it's just data about the data (metadata, if you want to impress people.)  This is a 'property' of the data.  'Members' of the string object we created are just the collection of methods and properties.

This leads to the key question for this post: how do we find out what's available?

PSH> Get-Member -InputObject $aString

   TypeName: System.String

Name             MemberType            Definition
----             ----------            ----------
Clone            Method                System.Object Clone()
CompareTo        Method                System.Int32 CompareTo(Object value),...
Contains         Method                System.Boolean Contains(String value)
CopyTo           Method                System.Void CopyTo(Int32 sourceIndex,...
EndsWith         Method                System.Boolean EndsWith(String value)...
Equals           Method                System.Boolean Equals(Object obj), Sy...
GetEnumerator    Method                System.CharEnumerator GetEnumerator()
GetHashCode      Method                System.Int32 GetHashCode()
GetType          Method                System.Type GetType()
GetTypeCode      Method                System.TypeCode GetTypeCode()
get_Chars        Method                System.Char get_Chars(Int32 index)
get_Length       Method                System.Int32 get_Length()
IndexOf          Method                System.Int32 IndexOf(Char value), Sys...
IndexOfAny       Method                System.Int32 IndexOfAny(Char[] anyOf)...
Insert           Method                System.String Insert(Int32 startIndex...
IsNormalized     Method                System.Boolean IsNormalized(), System...
LastIndexOf      Method                System.Int32 LastIndexOf(Char value),...
LastIndexOfAny   Method                System.Int32 LastIndexOfAny(Char[] an...
Normalize        Method                System.String Normalize(), System.Str...
PadLeft          Method                System.String PadLeft(Int32 totalWidt...
PadRight         Method                System.String PadRight(Int32 totalWid...
Remove           Method                System.String Remove(Int32 startIndex...
Replace          Method                System.String Replace(Char oldChar, C...
Split            Method                System.String[] Split(Params Char[] s...
StartsWith       Method                System.Boolean StartsWith(String valu...
Substring        Method                System.String Substring(Int32 startIn...
ToCharArray      Method                System.Char[] ToCharArray(), System.C...
ToLower          Method                System.String ToLower(), System.Strin...
ToLowerInvariant Method                System.String ToLowerInvariant()
ToString         Method                System.String ToString(), System.Stri...
ToUpper          Method                System.String ToUpper(), System.Strin...
ToUpperInvariant Method                System.String ToUpperInvariant()
Trim             Method                System.String Trim(Params Char[] trim...
TrimEnd          Method                System.String TrimEnd(Params Char[] t...
TrimStart        Method                System.String TrimStart(Params Char[]...
Chars            ParameterizedProperty System.Char Chars(Int32 index) {get;}
Length           Property              System.Int32 Length {get;}

Yuck.  That's a lot to swallow.  Relax, we won't cover each one.  We're interested in the simple fact that we have a way to inspect the members for a given String.  It's useful to get a listing of names, but the definitions are lacking.  The '...' means the line length (or $line.length if we want to get some practice thinking OO) is greater than 80 characters, so PowerShell is being nice (read: a pain) and truncating the line so the output looks nice.

Oh, and we're being lazy here by typing -i instead of -InputObject.  PowerShell is being nice (read: amazingly well thought out) and expanding -i to -InputObject because it's the only parameter to Get-Member that begins with 'i.'  If we had a cmdlet or function that could accept -InputObject and -IDontKnow, -i is no longer unambiguous.  Now, we need to specify '-in' or '-id'.  Okay, enough of that digression.

The reason PowerShell is inserting the '...' is because it, by default, formats any list (such as an object's members) to a table, pretty-printed for your command window.  Pretty useless, in this case.  Well, formatting it as a table is the default, but we can change it.

PSH> Get-Member -i $aString | Format-List

This specifies the output formatter to be a list.  It's still pretty-printed somewhat, but at least each member's definition is listed in full.  So we've taken a long list and made it even longer.  Great.  And I just wanted to look at the, let's say, Replace() member.  Do I put the replacement string first, or the string to be replaced first?  Is it

PSH> $aString.Replace("goodbye", "hello")

or

PSH> $aString.Replace("hello", "goodbye")

Of course we can try it, but for purposes of this exercise, let's see if we can get the definition for the Replace() method by itself.  Maybe if we limit it to only one member, it won't format it as a table.

PSH> Get-Member -i $aString | Where-Object { $_.name -eq "replace" }

PSH> Get-Member -i $aString -Name replace  # thanks to /\/\o\/\/ @ http://thepowershellguy.com

   TypeName: System.String

Name    MemberType Definition
----    ---------- ----------
Replace Method     System.String Replace(Char oldChar, Char newChar), System...

Same silly ellipsis, but we isolated the Replace() method.  Now, let's bring it together with the Format-List to see if we can cudgel PowerShell into doing what we want.

PSH> Get-Member -i $aString -Name "replace" | Format-List

TypeName   : System.String
Name       : Replace
MemberType : Method
Definition : System.String Replace(Char oldChar, Char newChar), System.String R
             eplace(String oldValue, String newValue)

Okay, target string first, then replacement string.  Still, there's an annoying linebreak at the ... 80 character mark.  (Thank you, PowerShell.  No, really.  Not.)  For our last trick, let's try to get the definition by itself, without that linebreak.

PSH> (gm -InputObject $aString -Name 'replace').Definition
System.String Replace(Char oldChar, Char newChar), System.String Replace(String oldValue, String newValue)

Yes.

To review, we learned three things:

  • We can use Get-Member to inspect any object for its member set (methods and properties.)
  • We can use Where-Object to isolate an object from a list based on its name.
  • We can override the default output formatter to overcome PowerShell's insistence on truncating lines.