Get-Hotfix

Get-Hotfix returns to PowerShell in PowerShell v7 preview 5 – at least on Windows.

PS> Get-Command Get-HotFix -Syntax

Get-HotFix [[-Id] <string[]>] [-ComputerName <string[]>] [-Credential <pscredential>] [<CommonParameters>]

Get-HotFix [-Description <string[]>] [-ComputerName <string[]>] [-Credential <pscredential>] [<CommonParameters>]

It still uses the Win32_QuickFixEngineering CIM class under the covers which is why its Windows only.

My preferred way of using Get-Hotfix is

PS> Get-HotFix  | Sort-Object -Property InstalledOn –Descending

That way I see the most recent fixes at the top of the list and its easier to determine if the system is up to date.

Posted in PowerShell 7 | Leave a comment

Resolve-Path

Resolve-Path is a cmdlet I haven’t used much – if at all – so I thought I should have a look at it.

At an arbitrary point in the file system:

PS> Get-Location

Path
—-
C:\Scripts\Modules\Coordinates

.. Indicates the next level up

PS> Get-ChildItem -Path ..

     Directory: C:\Scripts\Modules

etc

..\.. indicates two levels up

PS> Get-ChildItem -Path ..\..

     Directory: C:\Scripts

etc

Resolve-Path will resolve any use of wildcards and other characters with meaning into the full path

PS> Resolve-Path -Path ..

Path
—-
C:\Scripts\Modules

PS> Resolve-Path -Path ..\..

Path
—-
C:\Scripts

Note that

PS> Resolve-Path -Path .

Path
—-
C:\Scripts\Modules\Coordinates

is effectively the same as Get-Location

Posted in Powershell | Leave a comment

Ad hoc development

I was having a discussion about how people can learn PowerShell at the recent UK PowerShell day and mentioned ad hoc development. Surprisingly, no-one really knew what I meant.

Ad hoc development is a concept more than a development type. It was used extensively back in the days of PowerShell v1 and v2 but seems to have dropped off the radar these days.

Many people seem to learn the PowerShell language – either from a class or a book – but then don’t have any idea how to put that into practice. This is a failing of our teaching methods. Too many times I’ve seen people asking for help because they’ve dived into trying to create huge complicated scripts, or modules, and don’t have the background knowledge or experience to actually get the code to work.

Ad hoc development is one approach to moving from a basic knowledge of the PowerShell language to coding production level scripts and modules.

The starting point is the command line and working interactively. Use individual cmdlets or even a pipeline of cmdlets. If you find you’re using the same pipeline a lot then save as a script. Later, as you learn more you can parameterise the script, add all the production bells and whistles and even turn it into a module.

PowerShell is a huge beast these days with many parts you probably don’t need to start with. Use what you need now and add to your code as you learn rather then trying to jump right into a big complicated project. In the long run you’ll learn faster and end up getting more done with less frustration.

Posted in Powershell | Leave a comment

Pipeline Chain operators

Another experimental feature from PowerShell v7 preview 5 brings pipeline chain operators to PowerShell.

PS> Get-ExperimentalFeature -Name PSPipelineChainOperators | Format-List Name, Description

Name        : PSPipelineChainOperators

Description : Allow use of && and || as operators between pipeline invocations

The operators work as follows

<command1> && <command2>  means that command2 will fire if command1 completes without errors

You could write that in PowerShell now as

<command1>;  if ($?) { <command2> }

If command1 works and $? is $true (no errors) fire command2

<command1> || <command2>  means that command2 will fire if command1 has errors

i.e.

<command1>;  if (-not $?) { <command2> }

Like all experimental features it has to be enabled and PowerShell restarted

PS> Enable-ExperimentalFeature -Name PSPipelineChainOperators

WARNING: Enabling and disabling experimental features do not take effect until next start of PowerShell.

The best way to explain these operators is to show some examples

This is from the RFC

PS> 1,2,3 | ForEach-Object { $_ + 1 } && Write-Output ‘Hello’

2

3

4

Hello

PS> Get-Item -Path c:\nosuchfile -ErrorAction SilentlyContinue || Write-Output ‘ERROR’

ERROR

The message is written because the file isn’t found

PS> $path = ‘C:\test\DebugJob2.ps1’

PS> Get-Item -Path $path -ErrorAction SilentlyContinue && Remove-Item -Path $path

results in the file being deleted

Subsequently running

PS> Get-Item -Path $path -ErrorAction SilentlyContinue || Write-Output ‘No such file’

No such file

generates the message because the file isn’t there.

The chain operators work if there’s an error or not with the execution of command1 so you can’t use the test cmdlets such as Test-Path because they return booleans.

These operators follow the bash model of working on errors which isn’t necessarily the way PowerShell will work for you. I’m in two minds as to whether these operators are useful or not as the examples above feel contrived and I’m not convinced at the moment that the mental gymnastics required to accommodate  these operators in my code are worth it. Time will tell.

Posted in PowerShell 7 | Leave a comment

Get-Error

One of the experimental features new PowerShell v7 preview 5 is the Get-Error cmdlet. The features description states:

Enable Get-Error cmdlet that displays detailed information about ErrorRecords included nested objects

Enable the feature:

PS> Enable-ExperimentalFeature -Name Microsoft.PowerShell.Utility.PSGetError
WARNING: Enabling and disabling experimental features do not take effect until next start of PowerShell.

It would be better for users if the naming of experimental features became more consistent and ideally a single word rather than a fully qualified name including the module as this and some other new experimental features have.

The cmdlet has a simple syntax:

PS> Get-Command Get-Error -Syntax

Get-Error [-Newest <int>] [<CommonParameters>]

Get-Error [[-InputObject] <psobject>] [<CommonParameters>]

Generate some errors:

PS>  1/ 0
RuntimeException: Attempted to divide by zero.
PS>  Get-ChildItem -Path c:\nosuchfile
Get-ChildItem: Cannot find path ‘C:\nosuchfile’ because it does not exist.

Using Get-Error gives a very comprehensive view of the errors

PS> Get-Error

Exception             :
     ErrorRecord          :
         Exception             :
             Message : Cannot find path ‘C:\nosuchfile’ because it does not exist.
             HResult : -2146233087
         TargetObject          : C:\nosuchfile
         CategoryInfo          : ObjectNotFound: (C:\nosuchfile:String) [], ParentContainsErrorRecordException
         FullyQualifiedErrorId : PathNotFound
     ItemName             : C:\nosuchfile
     SessionStateCategory : Drive
     TargetSite           :
         Name          : GetChildItems
         DeclaringType : System.Management.Automation.SessionStateInternal
         MemberType    : Method
         Module        : System.Management.Automation.dll
     StackTrace           :
    at System.Management.Automation.SessionStateInternal.GetChildItems(String path, Boolean recurse, UInt32 depth,
CmdletProviderContext context)
    at System.Management.Automation.ChildItemCmdletProviderIntrinsics.Get(String path, Boolean recurse, UInt32 depth,
CmdletProviderContext context)
    at Microsoft.PowerShell.Commands.GetChildItemCommand.ProcessRecord()
     Message              : Cannot find path ‘C:\nosuchfile’ because it does not exist.
     Source               : System.Management.Automation
     HResult              : -2146233087
TargetObject          : C:\nosuchfile
CategoryInfo          : ObjectNotFound: (C:\nosuchfile:String) [Get-ChildItem], ItemNotFoundException
FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
InvocationInfo        :
     MyCommand        : Get-ChildItem
     ScriptLineNumber : 1
     OffsetInLine     : 2
     HistoryId        : 5
     Line             : Get-ChildItem -Path c:\nosuchfile
     PositionMessage  : At line:1 char:2
                        +  Get-ChildItem -Path c:\nosuchfile
                        +  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     InvocationName   : Get-ChildItem
     CommandOrigin    : Internal
ScriptStackTrace      : at <ScriptBlock>, <No file>: line 1
PipelineIterationInfo :

This is the latest error in the error collection. You’ll see the same information if you use

PS> $error[0]

If you use the Newest parameter you’ll also see the Error Index

PS> Get-Error -Newest 2

    ErrorIndex: 0

Exception             :
     ErrorRecord          :
         Exception             :
             Message : Cannot find path ‘C:\nosuchfile’ because it does not exist.
             HResult : -2146233087
         TargetObject          : C:\nosuchfile
         CategoryInfo          : ObjectNotFound: (C:\nosuchfile:String) [], ParentContainsErrorRecordException
         FullyQualifiedErrorId : PathNotFound
     ItemName             : C:\nosuchfile
     SessionStateCategory : Drive
     TargetSite           :
         Name          : GetChildItems
         DeclaringType : System.Management.Automation.SessionStateInternal
         MemberType    : Method
         Module        : System.Management.Automation.dll
     StackTrace           :
    at System.Management.Automation.SessionStateInternal.GetChildItems(String path, Boolean recurse, UInt32 depth,
CmdletProviderContext context)
    at System.Management.Automation.ChildItemCmdletProviderIntrinsics.Get(String path, Boolean recurse, UInt32 depth,
CmdletProviderContext context)
    at Microsoft.PowerShell.Commands.GetChildItemCommand.ProcessRecord()
     Message              : Cannot find path ‘C:\nosuchfile’ because it does not exist.
     Source               : System.Management.Automation
     HResult              : -2146233087
TargetObject          : C:\nosuchfile
CategoryInfo          : ObjectNotFound: (C:\nosuchfile:String) [Get-ChildItem], ItemNotFoundException
FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
InvocationInfo        :
     MyCommand        : Get-ChildItem
     ScriptLineNumber : 1
     OffsetInLine     : 2
     HistoryId        : 5
     Line             : Get-ChildItem -Path c:\nosuchfile
     PositionMessage  : At line:1 char:2
                        +  Get-ChildItem -Path c:\nosuchfile
                        +  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     InvocationName   : Get-ChildItem
     CommandOrigin    : Internal
ScriptStackTrace      : at <ScriptBlock>, <No file>: line 1
PipelineIterationInfo :

    ErrorIndex: 1

Exception             :
     ErrorRecord    :
         Exception             :
             Message : Attempted to divide by zero.
             HResult : -2146233087
         CategoryInfo          : NotSpecified: (:) [], ParentContainsErrorRecordException
         FullyQualifiedErrorId : RuntimeException
         InvocationInfo        :
             ScriptLineNumber : 1
             OffsetInLine     : 2
             HistoryId        : -1
             Line             : 1/ 0
             PositionMessage  : At line:1 char:2
                                +  1/ 0
                                +  ~~~~
             CommandOrigin    : Internal
         ScriptStackTrace      : at <ScriptBlock>, <No file>: line 1
     TargetSite     :
         Name          : Divide
         DeclaringType : System.Management.Automation.IntOps
         MemberType    : Method
         Module        : System.Management.Automation.dll
     StackTrace     :
    at System.Management.Automation.IntOps.Divide(Int32 lhs, Int32 rhs)
    at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
    at System.Management.Automation.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
     Message        : Attempted to divide by zero.
     Data           : System.Collections.ListDictionaryInternal
     InnerException :
         Message : Attempted to divide by zero.
         HResult : -2147352558
     Source         : System.Management.Automation
     HResult        : -2146233087
CategoryInfo          : NotSpecified: (:) [], RuntimeException
FullyQualifiedErrorId : RuntimeException
InvocationInfo        :
     ScriptLineNumber : 1
     OffsetInLine     : 2
     HistoryId        : -1
     Line             : 1/ 0
     PositionMessage  : At line:1 char:2
                        +  1/ 0
                        +  ~~~~
     CommandOrigin    : Internal
ScriptStackTrace      : at <ScriptBlock>, <No file>: line 1

This seems to give a concise view of the Index and the error

PS> Get-Error -Newest 2 | Select PSErrorIndex, @{N=’Error’; E={$_}} | Format-List

PSErrorIndex : 0
Error        : Cannot find path ‘C:\nosuchfile’ because it does not exist.

PSErrorIndex : 1
Error        : Attempted to divide by zero.

I think I’d rather see Get-Error by itself supply a list of current errors and their index. There should also be an Index parameter that enables you to pick a specific error. Otherwise this looks like a useful addition.

Posted in PowerShell 7 | Leave a comment

Returning cmdlets

If I’m interpreting the email updates coming from the PowerShell project the next code release of PowerShell v7 should see the following returning cmdlets:

Get-Counter

Update-List

Clear-RecycleBin

Out-Printer

All but Update-List are Windows only as far as I can ell

Posted in PowerShell 7 | Leave a comment

Error view

Error view is another experimental feature introduced with PowerShell v7 preveiw 5. The experimental feature needs to be enabled  and PowerShell restarted.

PS> Enable-ExperimentalFeature -Name PSErrorView
WARNING: Enabling and disabling experimental features do not take effect until next start of PowerShell.

At the PowerShell prompt you’d normally see an error in this form

PS> 1 / 0
Attempted to divide by zero.
At line:1 char:1
+ 1 / 0
+ ~~~~~
+ CategoryInfo          : NotSpecified: (:) [], RuntimeException
+ FullyQualifiedErrorId : RuntimeException

The PSErrorview feature changes this to a more concise one line error

PS> 1 / 0
RuntimeException: Attempted to divide by zero.

Enabling the experimental feature sets the $errorview preference variable to ConciseView

PS> $errorview
ConciseView

You can set the error view manually

PS> $errorview = [System.Management.Automation.ErrorView]::NormalView
PS> 1/0
Attempted to divide by zero.
At line:1 char:1
+ 1/0
+ ~~~
+ CategoryInfo          : NotSpecified: (:) [], RuntimeException
+ FullyQualifiedErrorId : RuntimeException

Possible values for $errorview are

PS> [enum]::GetValues([System.Management.Automation.ErrorView])
NormalView
CategoryView
ConciseView

In Normal view an error in a script looks like this

PS> .\test.ps1
Attempted to divide by zero.
At C:\Scripts\test.ps1:1 char:2
+  1 / 0
+  ~~~~~
+ CategoryInfo          : NotSpecified: (:) [], RuntimeException
+ FullyQualifiedErrorId : RuntimeException

In Concise view you get this

PS> $errorview = [System.Management.Automation.ErrorView]::ConciseView
PS> .\test.ps1
RuntimeException: C:\Scripts\test.ps1
Line |
        1 |  1 / 0
          |  ^ Attempted to divide by zero.

The concise view supplies a more obvious indication of where the error is occurring.

This is a new feature that looks useful especially if you spend a lot of time working interactively.

Posted in PowerShell 7 | Leave a comment