Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ It evaluates to `$true` if there's a returned process and `$false` if there isn'
perfectly valid to use pipeline expressions or other PowerShell statements like this:

```powershell
if ( Get-Process | Where Name -eq Notepad )
if ( Get-Process | where Name -EQ Notepad )
```

These expressions can be combined with each other with the `-and` and `-or` operators, but you may
Expand All @@ -452,7 +452,7 @@ in diving deeper, I have an article about [everything you wanted to know about $
I almost forgot to add this one until [Prasoon Karunan V][Prasoon Karunan V] reminded me of it.

```powershell
if ($process=Get-Process notepad -ErrorAction ignore) {$process} else {$false}
if ($process=Get-Process notepad -ErrorAction Ignore) {$process} else {$false}
```

Normally when you assign a value to a variable, the value isn't passed onto the pipeline or
Expand Down Expand Up @@ -643,7 +643,7 @@ fall into the `$snowSqlParam` in the correct place. Same holds true for the `$Pa
## Simplify complex operations

It's inevitable that you run into a situation that has way too many comparisons to check and your
`If` statement scrolls way off the right side of the screen.
`if` statement scrolls way off the right side of the screen.

```powershell
$user = Get-ADUser -Identity $UserName
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,11 @@ property, you get a `$null` value like you would for an undefined variable. It d
variable is `$null` or an actual object in this case.

```powershell
PS> $null -eq $undefined.some.fake.property
PS> $null -eq $undefined.Some.Fake.Property
True

PS> $date = Get-Date
PS> $null -eq $date.some.fake.property
PS> $null -eq $date.Some.Fake.Property
True
```

Expand All @@ -140,10 +140,10 @@ Calling a method on a `$null` object throws a `RuntimeException`.

```powershell
PS> $value = $null
PS> $value.toString()
PS> $value.ToString()
You cannot call a method on a null-valued expression.
At line:1 char:1
+ $value.tostring()
+ $value.ToString()
+ ~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
Expand Down Expand Up @@ -246,9 +246,9 @@ I ran into this issue when refactoring some code a few days ago. It had a basic
this.

```powershell
if ( $object.property )
if ( $object.Property )
{
$object.property = $value
$object.Property = $value
}
```

Expand All @@ -259,40 +259,40 @@ the property but it was a blank string value. This prevented it from ever gettin
previous logic. So I added a proper `$null` check and everything worked.

```powershell
if ( $null -ne $object.property )
if ( $null -ne $object.Property )
{
$object.property = $value
$object.Property = $value
}
```

It's little bugs like these that are hard to spot and make me aggressively check values for `$null`.

## $null.Count

If you try to access a property on a `$null` value, that the property is also `$null`. The `count`
If you try to access a property on a `$null` value, that the property is also `$null`. The `Count`
property is the exception to this rule.

```powershell
PS> $value = $null
PS> $value.count
PS> $value.Count
0
```

When you have a `$null` value, then the `count` is `0`. This special property is added by
When you have a `$null` value, then the `Count` is `0`. This special property is added by
PowerShell.

### [PSCustomObject] Count

Almost all objects in PowerShell have that count property. One important exception is the
`[PSCustomObject]` in Windows PowerShell 5.1 (This is fixed in PowerShell 6.0). It doesn't have a
count property so you get a `$null` value if you try to use it. I call this out here so that you
don't try to use `.Count` instead of a `$null` check.
Almost all objects in PowerShell have that `Count` property. One important exception is the
`[pscustomobject]` in Windows PowerShell 5.1 (This is fixed in PowerShell 6.0). It doesn't have a
`Count` property so you get a `$null` value if you try to use it. I call this out here so that you
don't try to use `Count` instead of a `$null` check.

Running this example on Windows PowerShell 5.1 and PowerShell 6.0 gives you different results.

```powershell
$value = [PSCustomObject]@{Name='MyObject'}
if ( $value.count -eq 1 )
$value = [pscustomobject]@{Name='MyObject'}
if ( $value.Count -eq 1 )
{
"We have a value"
}
Expand All @@ -318,19 +318,19 @@ required, the value is always `$null`. But if you place it inside an array, it's
an empty array.

```powershell
PS> $containempty = @( @() )
PS> $containnothing = @($nothing)
PS> $containnull = @($null)
PS> $containEmpty = @( @() )
PS> $containNothing = @($nothing)
PS> $containNull = @($null)

PS> $containempty.count
PS> $containEmpty.Count
0
PS> $containnothing.count
PS> $containNothing.Count
0
PS> $containnull.count
PS> $containNull.Count
1
```

You can have an array that contains one `$null` value and its `count` is `1`. But if you place
You can have an array that contains one `$null` value and its `Count` is `1`. But if you place
an empty array inside an array then it's not counted as an item. The count is `0`.

If you treat the enumerable null like a collection, then it's empty.
Expand All @@ -355,7 +355,7 @@ Depending on your code, you should account for the `$null` in your logic.

Either check for `$null` first

- Filter out null on the pipeline (`... | Where {$null -ne $_} | ...`)
- Filter out null on the pipeline (`... | where {$null -ne $_} | ...`)
- Handle it in the pipeline function

## foreach
Expand All @@ -372,7 +372,7 @@ foreach ( $node in $null )
This saves me from having to `$null` check the collection before I enumerate it. If you have a
collection of `$null` values, the `$node` can still be `$null`.

The foreach started working this way with PowerShell 3.0. If you happen to be on an older version,
The `foreach` started working this way with PowerShell 3.0. If you happen to be on an older version,
then this is not the case. This is one of the important changes to be aware of when back-porting
code for 2.0 compatibility.

Expand Down Expand Up @@ -420,7 +420,7 @@ it.
function Do-Something
{
param(
[String] $Value
[string] $Value
)
}
```
Expand Down Expand Up @@ -485,8 +485,8 @@ commands you use deal with the no results and error scenarios.
## Initializing to $null

One habit that I have picked up is initializing all my variables before I use them. You are required
to do this in other languages. At the top of my function or as I enter a foreach loop, I define all
the values that I'm using.
to do this in other languages. At the top of my function or as I enter a `foreach` loop, I define
all the values that I'm using.

Here is a scenario that I want you to take a close look at. It's an example of a bug I had to chase
down before.
Expand All @@ -498,7 +498,7 @@ function Do-Something
{
try
{
$result = Get-Something -ID $node
$result = Get-Something -Id $node
}
catch
{
Expand All @@ -521,7 +521,7 @@ to `$result`. It fails before the assignment so we don't even assign `$null` to
variable. `$result` still contains the previous valid `$result` from other iterations.
`Update-Something` to execute multiple times on the same object in this example.

I set `$result` to `$null` right inside the foreach loop before I use it to mitigate this issue.
I set `$result` to `$null` right inside the `foreach` loop before I use it to mitigate this issue.

```powershell
foreach ( $node in 1..6 )
Expand Down Expand Up @@ -557,7 +557,7 @@ function Do-Something
{
try
{
$result = Get-Something -ID $node
$result = Get-Something -Id $node
}
catch
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ better idea of what that means.

## Creating a PSCustomObject

I love using `[PSCustomObject]` in PowerShell. Creating a usable object has never been easier.
I love using `[pscustomobject]` in PowerShell. Creating a usable object has never been easier.
Because of that, I'm going to skip over all the other ways you can create an object but I need
to mention that most of these examples are PowerShell v3.0 and newer.

```powershell
$myObject = [PSCustomObject]@{
$myObject = [pscustomobject]@{
Name = 'Kevin'
Language = 'PowerShell'
State = 'Texas'
Expand All @@ -32,7 +32,7 @@ $myObject = [PSCustomObject]@{

This method works well for me because I use hashtables for just about everything. But there are
times when I would like PowerShell to treat hashtables more like an object. The first place you
notice the difference is when you want to use `Format-Table` or `Export-CSV` and you realize that a
notice the difference is when you want to use `Format-Table` or `Export-Csv` and you realize that a
hashtable is just a collection of key/value pairs.

You can then access and use the values like you would a normal object.
Expand Down Expand Up @@ -73,18 +73,18 @@ $myHashtable = @{
State = 'Texas'
}

$myObject = New-Object -TypeName PSObject -Property $myHashtable
$myObject = New-Object -TypeName psobject -Property $myHashtable
```

This way is quite a bit slower but it may be your best option on early versions of PowerShell.

### Saving to a file

I find the best way to save a hashtable to a file is to save it as JSON. You can import it back into
a `[PSCustomObject]`
a `[pscustomobject]`

```powershell
$myObject | ConvertTo-Json -depth 1 | Set-Content -Path $Path
$myObject | ConvertTo-Json -Depth 1 | Set-Content -Path $Path
$myObject = Get-Content -Path $Path | ConvertFrom-Json
```

Expand All @@ -108,7 +108,7 @@ $myObject.ID
You can also remove properties off of an object.

```powershell
$myObject.psobject.properties.remove('ID')
$myObject.psobject.Properties.Remove('ID')
```

The `.psobject` is an intrinsic member that gives you access to base object metadata. For more
Expand All @@ -120,13 +120,13 @@ information about intrinsic members, see
Sometimes you need a list of all the property names on an object.

```powershell
$myObject | Get-Member -MemberType NoteProperty | Select -ExpandProperty Name
$myObject | Get-Member -MemberType NoteProperty | select -ExpandProperty Name
```

We can get this same list off of the `psobject` property too.

```powershell
$myobject.psobject.properties.name
$myobject.psobject.Properties.Name
```

> [!NOTE]
Expand Down Expand Up @@ -163,7 +163,7 @@ from them.

```powershell
$hashtable = @{}
foreach( $property in $myobject.psobject.properties.name )
foreach( $property in $myobject.psobject.Properties.Name )
{
$hashtable[$property] = $myObject.$property
}
Expand All @@ -178,10 +178,10 @@ if( $null -ne $myObject.ID )
```

But if the value could be `$null` you can check to see if it exists by checking the
`psobject.properties` for it.
`psobject.Properties` for it.

```powershell
if( $myobject.psobject.properties.match('ID').Count )
if( $myobject.psobject.Properties.Match('ID').Count )
```

## Adding object methods
Expand All @@ -193,7 +193,7 @@ If you need to add a script method to an object, you can do it with `Add-Member`
```powershell
$ScriptBlock = {
$hashtable = @{}
foreach( $property in $this.psobject.properties.name )
foreach( $property in $this.psobject.Properties.Name )
{
$hashtable[$property] = $this.$property
}
Expand Down Expand Up @@ -236,21 +236,21 @@ Object variables hold a reference to the actual object. When you assign one obje
variable, they still reference the same object.

```powershell
$third = [PSCustomObject]@{Key=3}
$third = [pscustomobject]@{Key=3}
$fourth = $third
$fourth.Key = 4
```

Because `$third` and `$fourth` reference the same instance of an object, both `$third.key` and
`$fourth.Key` are 4.

### psobject.copy()
### psobject.Copy()

If you need a true copy of an object, you can clone it.

```powershell
$third = [PSCustomObject]@{Key=3}
$fourth = $third.psobject.copy()
$third = [pscustomobject]@{Key=3}
$fourth = $third.psobject.Copy()
$fourth.Key = 4
```

Expand All @@ -267,14 +267,14 @@ obvious. First thing we need to do is give it a `PSTypeName`. This is the most c
people do it:

```powershell
$myObject.PSObject.TypeNames.Insert(0,"My.Object")
$myObject.psobject.TypeNames.Insert(0,"My.Object")
```

I recently discovered another way to do this from Redditor `u/markekraus`. He talks about this
approach that allows you to define it inline.

```powershell
$myObject = [PSCustomObject]@{
$myObject = [pscustomobject]@{
PSTypeName = 'My.Object'
Name = 'Kevin'
Language = 'PowerShell'
Expand Down Expand Up @@ -337,7 +337,7 @@ $TypeData = @{
TypeName = 'My.Object'
MemberType = 'ScriptProperty'
MemberName = 'UpperCaseName'
Value = {$this.Name.toUpper()}
Value = {$this.Name.ToUpper()}
}
Update-TypeData @TypeData
```
Expand Down Expand Up @@ -388,7 +388,7 @@ to the pipe when they shouldn't.

## Closing thoughts

The context of this was all about `[PSCustomObject]`, but a lot of this information applies to
The context of this was all about `[pscustomobject]`, but a lot of this information applies to
objects in general.

I have seen most of these features in passing before but never saw them presented as a collection of
Expand Down
Loading