Monthly Archive

Categories

PowerShell v5

Long file paths

Long file paths – greater than 260 characters – have been a pain to deal with in Windows.

 

There is an argument that you should avoid file paths of that length but sometimes you don’t have any choice – when you inherit a file structure for instance.

 

The following cmdlets from the NTFSsecurity module I recently highlighted are designed to work with long file paths.

Copy-Item2
Get-ChildItem2
Get-FileHash2
Get-Item2
Move-Item2
Remove-Item2
Test-Path2

 

Hopefully, this will make dealing with long file paths easier in the future

NTFSsecurity module

Just came across the NTFSsecurity module. Its available on the PowerShell gallery or from https://github.com/raandree/NTFSSecurity.

The *-Acl cmdlets have been around since Windows PowerShell v1 but aren’t easy to use and don’t cover all our needs.

 

This module contains a number of cmdlets:

Add-NTFSAccess
Add-NTFSAudit
Clear-NTFSAccess
Clear-NTFSAudit
Copy-Item2
Disable-NTFSAccessInheritance
Disable-NTFSAuditInheritance
Disable-Privileges
Enable-NTFSAccessInheritance
Enable-NTFSAuditInheritance
Enable-Privileges
Get-ChildItem2
Get-DiskSpace
Get-FileHash2
Get-Item2
Get-NTFSAccess
Get-NTFSAudit
Get-NTFSEffectiveAccess
Get-NTFSHardLink
Get-NTFSInheritance
Get-NTFSOrphanedAccess
Get-NTFSOrphanedAudit
Get-NTFSOwner
Get-NTFSSecurityDescriptor
Get-NTFSSimpleAccess
Get-Privileges
Move-Item2
New-NTFSHardLink
New-NTFSSymbolicLink
Remove-Item2
Remove-NTFSAccess
Remove-NTFSAudit
Set-NTFSInheritance
Set-NTFSOwner
Set-NTFSSecurityDescriptor
Test-Path2

 

Some of the cmdlets appear to overlap with standard PowerShell cmdlets

Copy-Item2
Get-ChildItem2
Get-FileHash2
Get-Item2
Move-Item2
Remove-Item2
Test-Path2

which needs further investigation.

PS> get-command Test-Path -Syntax

Test-Path [-Path] <string[]> [-Filter <string>] [-Include <string[]>]
[-Exclude <string[]>] [-PathType <TestPathType>] [-IsValid]
[-Credential <pscredential>] [-UseTransaction] [-OlderThan <datetime>]
[-NewerThan <datetime>] [<CommonParameters>]

Test-Path -LiteralPath <string[]> [-Filter <string>] [-Include <string[]>]
[-Exclude <string[]>] [-PathType <TestPathType>] [-IsValid]
[-Credential <pscredential>] [-UseTransaction] [-OlderThan <datetime>]
[-NewerThan <datetime>] [<CommonParameters>]

PS> get-command Test-Path2 -Syntax

Test-Path2 [-Path] <string[]> [-PathType <TestPathType>] [<CommonParameters>]

 

There isn’t a complete set of help files for the module at the moment but some documentation is available through the github repository.

 

This is a Windows PowerShell only module at the moment as it requires System.Windows.Forms but may work on PowerShell core through the Windows compatibility module.

Group-Object change in PowerShell v6.1

There’s a subtle Group-Object change in PowerShell v6.1.

In PowerShell v5.1 if you do this:

$ts = 'ffrluoluntlvxutxbdvbktgyyvsvcrkxoyfotzkzogcwuwycmnhuedk'
$ts.ToCharArray() | group

 

You get this:

Count Name Group
----- ---- -----
    3    f {f, f, f}
    2    r {r, r}
    3    l {l, l, l}
    5    u {u, u, u, u...}
    4    o {o, o, o, o}
    2    n {n, n}
    4    t {t, t, t, t}
    4    v {v, v, v, v}
    3    x {x, x, x}
    2    b {b, b}
    2    d {d, d}
    4    k {k, k, k, k}
    2    g {g, g}
    4    y {y, y, y, y}
    1    s {s}
    3    c {c, c, c}
    2    z {z, z}
    2    w {w, w}
    1    m {m}
    1    h {h}
    1    e {e}

 

If you repeat the exercise in PowerShell v6.1 you get this:

Count Name Group
----- ---- -----
    2    b {b, b}
    3    c {c, c, c}
    2    d {d, d}
    1    e {e}
    3    f {f, f, f}
    2    g {g, g}
    1    h {h}
    4    k {k, k, k, k}
    3    l {l, l, l}
    1    m {m}
    2    n {n, n}
    4    o {o, o, o, o}
    2    r {r, r}
    1    s {s}
    4    t {t, t, t, t}
    5    u {u, u, u, u...}
    4    v {v, v, v, v}
    2    w {w, w}
    3    x {x, x, x}
    4    y {y, y, y, y}
    2    z {z, z}

 

In PowerShell v6.1 the results are automatically sorted by the Name (the same behaviour is exhibited in PowerShell v6.2 preview 3). That may, or may not, be what you want. Not sure exactly when, or even why, this behaviour changed.

 

When I’m using Group-Object I’m usually looking for the most or least common item so I tend to sort on value.

 

I don’t understand the value of this change and suspect its yet another change to PowerShell that was introduced to meet someone’s pet view of the world – one of the major problems of open source projects no one seems to really apply the “just because we can doesn’t mean we should filter”.

Finding the minimum and maximum values

Finding the minimum and maximum values in a set of numbers is ridiculously easy with PowerShell.

I’ve created a function in line with the other techniques I’ve shown but in reality could be be done equally well as a single line of code:

function get-minmax {
[CmdletBinding()]
param (
[int[]]$iarray
)

$mm = $iarray | Measure-Object -Minimum -Maximum

New-Object -TypeName PSobject -Property @{
Minimum = $mm.Minimum
Maximum = $mm.Maximum
}
}

 

Just pipe the array into Measure-Object and use the –Minimum and –Maximum parameters as shown. I created an output object for easier handling if you want to do anything else to the array.

 

You can also get the sum and average of the array with Measure-Object. PowerShell v6.1 adds the Standard Deviation and an –Allstats parameter so you don’t need to specify each individual option:

PS> $iarray = 1,2,3,4,23,5,6,7,8,9,10,23,11,12,13,7,14,15,16,17,18,20,21,22,11,23,24,25
PS> $iarray | Measure-Object -AllStats

Count : 28
Average : 13.2142857142857
Sum : 370
Maximum : 25
Minimum : 1
StandardDeviation : 7.46030057411125
Property :

PowerShell new line

A PowerShell new line can be generated using `n. It’s one of a series of special characters recognised by PowerShell.

The full list of Windows PowerShell v5.1 special characters is:

`0 Null
`a Alert
`b Backspace
`f Form feed
`n New line
`r Carriage return
`t Horizontal tab
`v Vertical tab
--% Stop parsing

 

Here’s some examples:

`0 is a null character – empty space

PS> "abcd`0efg"
abcd efg

 

`a causes the machine to issue an alert – beep

PS> "`a Beep "; "`a Beep"
Beep
Beep

Two alerts are sent but you might only hear one.

 

The backspace character moves the cursor back one space overwriting the character that was there:

PS> "abcd`befg"
abcefg

 

Form feed is for printers only

 

A new line is added by `n

PS> "abc`nde`nfg"
abc
de
fg

 

The carriage return returns the cursor to the beginning of the line so any text before it will be overwrittem

PS> "This original text`rIs overwritten by this brand new text"
Is overwritten by this brand new text

 

A horizontal tab is added by `t

PS> "This`twill`ttab`tacross`tthe`tscreen"
This will tab across the screen

 

And `v for vertical tabs but only when printing documents – it doesn’t work on screen output

 

You can use –% to stop interpreting input as PowerShell commands or expressions. In this case PowerShell attempts to evaluate $x

PS> Write-Host $x = a variable
= a variable

but what you really want is

PS> Write-Host --% $x = a variable
--% $x = a variable

Unfortunately, you also get the –% come through.

The about_parsing help file has an example using icacls.

 

You’ll have noticed that all of the examples were in double quotes – single quotes stop the special character being recognised:

PS> "This`twill`ttab`tacross`tthe`tscreen"
This will tab across the screen
PS> 'This`twill`ttab`tacross`tthe`tscreen'
This`twill`ttab`tacross`tthe`tscreen

 

PowerShell v6 introduced `u for working with unicode characters

PS> "`u{2195}"

you can have 1 – 6 hex chacraters with a maximim value of 10FFFF

Update-Help difference

Between Windows PowerShell v5.1 and PowerShell v6.x I’ve noticed an Update-Help difference.

 

In Windows PowerShell v5.1 I can just do this:

Update-Help -Force

from an elevated prompt.

 

In PowerShell v6.0 and V6.1 previews I have to give the UI culture I want

Update-Help -UICulture en-US –Force

 

All instances – PowerShell v5.1, v6.0.3 and v6.1 preview 4 are running on a single Windows 10 machine. The UI culture is en-GB for all PowerShell instances and versions.

Not a major problem now I’ve tried using the en-US culture but it explains why I wasn’t getting help before.

PowerShell version

Discovering the PowerShell version you’re using can be an interesting task.

The automatic variable $PSVersionTable was introduced in PowerShell v2. On my Windows 10 version 1803 machine for Windows PowerShell I get

PS> $PSVersionTable

Name Value
---- -----
PSVersion 5.1.17134.48
PSEdition Desktop
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
BuildVersion 10.0.17134.48
CLRVersion 4.0.30319.42000
WSManStackVersion 3.0
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1

 

$PSVersionTable is a hashtable so the order in which items are displayed may vary. The contents of the hashtable have changed over time as well.

 

The important parts of $PsversionTable include:

The version of PowerShell itself

PS> $PSVersionTable.PSVersion

Major Minor Build Revision
----- ----- ----- --------
5 1 17134 48

PS> $PSVersionTable.PSVersion.Major
5

This a simple test for version.

 

The edition is also important

PS> $PSVersionTable.PSEdition
Desktop

Desktop means its full CLR – in other words Windows PowerShell

 

The WSMAN version is also important

PS> $PSVersionTable.WSManStackVersion

Major Minor Build Revision
----- ----- ----- --------
3 0 -1 -1

You need v3 to use CIM sessions.

 

With PowerShell v6 you get a few more options

PS> $PSVersionTable

Name Value
---- -----
PSVersion 6.0.1
PSEdition Core
GitCommitId v6.0.1
OS Microsoft Windows 10.0.17134
Platform Win32NT
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
WSManStackVersion 3.0

 

Notice PSEdition is set to Core.

The OS and Platform data are v6 only

The PSRemotingProtocolVersion, SerializationVersion and WSManStackVersion are the same for Windows PowerShell v5.1 and PowerShell v6.

 

PowerShell v6 runs on Windows, Linux and macOS. You can test which OS you’re on from $PSVersionTable or more simply using the following automatic variables:

PS> ls variable:\is*

Name Value
---- -----
IsLinux False
IsMacOS False
IsWindows True
IsCoreCLR True

Using these you could create branching logic to perform a task by calling the appropriate command based on the underlying operating system.

Controlled zip

Powershell v5 introduced the Compress- and Expand-Archive cmdlets which enabled you to manage compressed archives. I had a question about how you could control adding files to archives using a CSV file. This is how you do a controlled zip.

 

Start by creating a set of test data.

1..100 |
foreach {
$file = "File$psitem.txt"
Get-Process | Out-File -FilePath $file

$i = Get-Random -Minimum 1 -Maximum 4
$zip = "Archive$i.zip"

$props = @{
FileName = $file
Archive = $zip
}

New-Object -TypeName PSObject -Property $props
} | Export-Csv -Path FilesToArchive.CSV –NoTypeInformation

 

I created a 100 files – name of the form FileN.txt and into each piped the output of Get-Process just so they weren’t empty.

I wanted 3 zip files – named ArchiveN.zip

I used Get-Random to assign the zip file.

Create an object to with the file and archive and output to CSV

 

The CSV looks like this:

FileName Archive
-------- -------
File1.txt Archive1.zip
File2.txt Archive3.zip
File3.txt Archive1.zip
File4.txt Archive2.zip
File5.txt Archive1.zip
File6.txt Archive3.zip

 

To perform the zip

Import-Csv .\FilesToArchive.CSV |
foreach {
Compress-Archive -Path $_.FileName -DestinationPath $_.Archive -Update
}

 

Read the CSV file and for each file add it to the appropriate archive. The –Update parameter on Compress-Archive is what allows you to add files to an existing archive.

Clear-RecycleBin

Every so often I find a new cmdlet in PowerShell. This was the case with Clear-RecycleBin that I’ve just found.

It appears to have been introduced with PowerShell 5.0 BUT isn’t available in PowerShell 6.0

With pretty simple syntax

PS> Get-Command Clear-RecycleBin -Syntax

Clear-RecycleBin [[-DriveLetter] <string[]>] [-Force] [-WhatIf] [-Confirm] [<CommonParameters>]

Its easy to use

PS> Clear-RecycleBin -DriveLetter C -Force

use –Force to avoid the question to confirm the action

Updating built in modules

Windows 10 and Server 2016 automatically install a module called Pester which is used for testing code. Its the foundation of Test Driven Development or Behaviour Driven Development using PowerShell.

The version  installed by default is 3.4.0.

Pester is originally an open source module that has been incorporated into Windows. The latest version from the PowerShell Gallery is 4.0.2

Normally you’d use Update-Module to install the new version BUT you didn’t install pester from the gallery using Install-Module so you’ll get a big fat error message.

The answer is to use

Install-Module pester –Force

You might still get an error message about the Pester module not being catalog signed. if you do and still want the latest version then use

Install-Module pester -Force -SkipPublisherCheck