Monthly Archive

Categories

Monthly Archives: December 2018

PowerShell articles

Here’s some links for other PowerShell articles I’ve written recently

https://searchwindowsserver.techtarget.com/tip/PowerShell-commands-to-copy-files-Basic-to-advanced-methods

https://searchwindowsserver.techtarget.com/tutorial/PowerShell-commands-for-Active-Directory-Groups-management

https://searchwindowsserver.techtarget.com/tip/PowerShell-Core-61-offers-many-small-improvements

https://searchwindowsserver.techtarget.com/tutorial/How-PowerShell-Direct-helps-polish-off-those-VMs

https://searchwindowsserver.techtarget.com/tutorial/Working-with-PowerShell-module-downloads-from-the-gallery

Enjoy

Change file times

When a file is created, accessed or modified that time is recorded. This is how to change file times.

There are three properties to consider:

CreationTime

LastAccessTime

LastWriteTime

 

Just to add to the fun there are UTC (GMT in real world) times as well:

CreationTimeUtc

LastAccessTimeUtc

LastWriteTimeUtc

 

If you need to change a property its a set operation. For instance

Get-ChildItem -Path x9.txt | foreach {$_.LastWriteTime = ($_.LastWriteTime).AddHours(6)}

 

Substitute the appropriate property as required. If you change a property the UTC version is changed in step – accounting for timezone differences

Get first non-repeating character in a string

How do you get first non-repeating character in a string.

 

In Windows PowerShell v5.1 you could use Group-Object but as I showed last time that approach has been taken away in PowerShell v6.1.

 

Instead you need to loop through the characters in the string and count them

function get-firstnonrepeatcharacter {
[CmdletBinding()]
param (
[string]$teststring
)

$a = [ordered]@{}

$teststring.ToLower().ToCharArray() |
foreach {
if ($a.$psitem) {
$a.$psitem += 1
}
else {
$a += @{$psitem = 1}
}

}

$a.GetEnumerator() | where Value -eq 1 | select -First 1

}

 

Input the test string through a parameter. Create an ORDERED hash table for the results – you need to preserve the order in which characters are counted!

 

Convert the string to lowercase (remove this step if your worried about case) and convert to an array of chars. Pipe into foreach-object. Test is the character is already in the hashtable. If it is increment the count. If it isn’t add it.

 

Use the getEnumerator() method to access the hash table. use where-object to filter for characters only appearing once. Select the first one.

 

$ts = 'lcoauywfnqumfheolfzaliigngulxqdbnzprnylfqvsxathrqsyjowm'

PS> get-firstnonrepeatcharacter -teststring $ts

Name Value
----      -----
c          1

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”.

Counting vowels

If you’re given a string how would you go about counting vowels, consonants and non-alphabet characters.

 

My approach would be:

 

function measure-vowel {
[CmdletBinding()]
param (
[string]$teststring
)

$counts = [ordered]@{
Vowels = 0
Consonants = 0
NonAlphabet = 0
}

$vowels = 97, 101, 105, 111, 117

$teststring.ToLower().ToCharArray() |
foreach {
$test = [byte]$psitem

switch ($test) {
{$test -in $vowels} {$counts.Vowels += 1; break}
{$test -ge 97 -and $test -le 122} {$counts.Consonants += 1; break}
default {$counts.NonAlphabet += 1; break}
}

}

New-Object -TypeName PSObject -Property $counts

}

 

The string to test is an input parameter. Set up the output hash table and the byte value of the vowels.

 

Convert the string to lowercase and then to a char array which is put on the pipeline. Convert the char to a byte value and then use the switch statement to determine if its a vowel, consonant or non-alphabetic character.

 

Finally output the results.

Create a random string

I often need to create a random string of characters. Here’s a simple function to achieve that:

 

function get-randomstring {
[CmdletBinding()]
param (
[int]$length
)

$rca = 1..$length |
foreach {
$ran = Get-Random -Minimum 97 -Maximum 123
[char][byte]$ran
}

$rca -join ''

}

 

The required length is passed as an integer parameter. Use the range operator as a counter and in Foreach-Object generate a random number between 97 and 122. Convert that to a char (a=97 and z-122) and add to the array. Join the array of chars to create the string.

Return of the missing PSDiagnostics

Return of the missing PSDiagnostics members in PowerShell v6.2 preview 3.

 

In Windows PowerShell v5.1 the PSDiagnostics module contains these members:

Disable-PSTrace
Disable-PSWSManCombinedTrace
Disable-WSManTrace
Enable-PSTrace
Enable-PSWSManCombinedTrace
Enable-WSManTrace
Get-LogProperties
Set-LogProperties
Start-Trace
Stop-Trace

 

In PowerShell v6.1.1 you just get these:

Disable-PSTrace
Enable-PSTrace
Get-LogProperties
Set-LogProperties

 

Quite a few missing.

In PowerShell v6.2 preview 3 we’re back to the original set:

Disable-PSTrace
Disable-PSWSManCombinedTrace
Disable-WSManTrace
Enable-PSTrace
Enable-PSWSManCombinedTrace
Enable-WSManTrace
Get-LogProperties
Set-LogProperties
Start-Trace
Stop-Trace

 

NOTE: this is just on Windows – I don’t have a non-Windows machine to determine what’s available on non-Windows platforms

Find duplicate characters in a string

Staying with our current theme of manipulating strings this is how you find duplicate characters in a string.

function get-duplicatechar {
[CmdletBinding()]
param (
[string]$teststring
)

$teststring.ToCharArray() |
Group-Object -NoElement |
where Count -gt 1 |
Sort-Object -Property Count -Descending

}

 

Convert the string to a char array, group on the characters and use Where-Object to filter the characters that occur more than once – the duplicates. Sort the output for easier viewing.

Join-String

Join-String is a new cmdlet in PowerShell v6.2 preview 3. Join-String enables you to use the pipeline to join strings.

 

We’ve had the –join operator for a long time:

PS> 1..3 -join ','
1,2,3

 

As an alternative you could do

PS> 1..3 | Join-String -Separator ','
1,2,3

 

You can add a prefix and suffix to the final string:

PS> 1..3 | Join-String -OutputPrefix 'A' -OutputSuffix 'b' -Separator ','
A1,2,3b

 

You can use the property of objects on the pipeline as a source for the data:

PS> Get-ChildItem -Path C:\Scripts\Techniques\ | select -ExpandProperty Basename | Join-String -Separator ','
arrays,numbertechniques,stringtechniques

 

Quotes – single or double – can be added

PS> Get-ChildItem -Path C:\Scripts\Techniques\ | select -ExpandProperty Basename | Join-String -Separator ',' -SingleQuote
'arrays','numbertechniques','stringtechniques'

PS> Get-ChildItem -Path C:\Scripts\Techniques\ | select -ExpandProperty Basename | Join-String -Separator ',' -DoubleQuote
"arrays","numbertechniques","stringtechniques"

 

The current culture can also be use to influence the string. For instance dates:

PS> Get-Date

11 December 2018 16:25:43

PS> Get-Date | Join-String
12/11/2018 16:25:52

 

To me the string version of the date reads 12 November 2018 not 11 December 2018 so I need to use my culture

PS> Get-Date | Join-String -UseCulture
11/12/2018 16:26:54

 

And now it looks correct.

 

I suspect that Join-String will become one of those cmdlet that has many, many uses. For instance an easy way to create sequential file names:

PS> 1..5 | foreach {$_ | Join-String -OutputPrefix 'File' -OutputSuffix '.txt'}
File1.txt
File2.txt
File3.txt
File4.txt
File5.txt

PowerShell v6.2 preview 3 install issue

PowerShell v6.2 preview 3 install issue - PowerShell v6.2 preview 3 is now available from https://github.com/PowerShell/PowerShell/releases but you may notice a probloem if you install over the top of PowerShell v6.2 preview 2.

 

When you click on the icon to start preview 3 the console will flash open and then immediately close.

 

The fix is to either uninstall preview 2 before installing preview 3 OR install preview 3 over the top of preview 2 and immediately rerun the preview 3 installation selecting the repair option. The install will work through and then PowerShell v6.2 preview 3 will work.

 

The underlying cause is understood and hopefully will be fixed in a later release