PowerShell for Windows Admins


September 23, 2018  5:16 AM

PowerShell copy file

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

An article I wrote for searchwindowsserver on PowerShell file copy was published recently.

The article is available at

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

September 22, 2018  5:24 AM

Determining the PowerShell host

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

The PowerShell ecosystem is more diverse than it used to be which makes determining the PowerShell host a bit more difficult.

The variable $host, and its Name property, is a good starting point.

PS>  $host.Name
ConsoleHost

 

You get ConSoleHost if you’re running in the console.

For ISE you get –  Windows PowerShell ISE Host

And for VScode you get  –  Visual Studio Code Host

 

If you want to know the PowerShell version as well it’s a bit more awkward.

$host.Version works for the console and ISE as it returns the PowerShell version. In VScode however it returns the version of VScode.  The best route is to use

$PSVersionTable.PSVersion

 

While you’re at it you may want to check the edition of PowerShell – Core or Desktop

PS>  $PSVersionTable.PSEdition
Core

To make that a bit easier

function get-powershellhost {

New-Object -TypeName PSobject -Property @{
Hostname = $host.Name
PSversion = $PSVersionTable.PSVersion
PSEdition = $PSVersionTable.PSEdition
}

}

 

The function runs in the console, ISE and VScode and was tested across Windows PowerShell v5.1 and PowerShell v6.1

Feel free to add other information as required


September 21, 2018  5:32 AM

Splitting paths

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

PowerShell has the Split-Path cmdlet that provides the leaf and parent of a path. But what if you’re splitting paths and need one or paths at a higher level.

Consider the path

PS> $path = 'C:\Scripts\HyperV\Admin\Optimize-VMDisks.ps1'

 

Its just an arbitrary path from my test machine.

Using Split-Path you can get the parent (by default) or the leaf path

PS> Split-Path -Path $path
C:\Scripts\HyperV\Admin
PS> Split-Path -Path $path -Parent
C:\Scripts\HyperV\Admin
PS> Split-Path -Path $path -Leaf
Optimize-VMDisks.ps1

 

But what if you want the grandfather path. That starts to get ugly just using Split-Path.

PS> Split-Path -Path (Split-Path -Path $path -Parent)
C:\Scripts\HyperV

 

The code gets uglier and uglier as you progress up the path

I decided I needed an object whose properties gave me a consistent view of the path hierarchy – the parent path was always level 1; the grandfather was always level 2 etc. Something like this

Level00 : C:\Scripts\HyperV\Admin\Optimize-VMDisks.ps1
Level01 : C:\Scripts\HyperV\Admin
Level02 : C:\Scripts\HyperV
Level03 : C:\Scripts
Level04 : C:\

 

My solution was to create this function

function split-multipath {
  [CmdletBinding()]
  param (
    [string]$path
  )

  if (-not (Test-Path -Path $path -IsValid)) {
    throw "Invalid path: $path" 
  }

  $outpaths = [ordered]@{
    Level00 = $path
  }

  $l = 1

  $path = Split-Path -Path $path  -Parent

  while ($path -ne ''){
    $level = "Level{0:00}" -f $l
    $outpaths += @{
       $level = $path
    }
    
    $l++
    $path = Split-Path -Path $path  -Parent

  }

  New-Object -TypeName PSobject -Property $outpaths

}

 

The split-multipath function takes a string as a parameter. That string is tested to determine if its a valid path – that could be moved to a ValidateScript test on the parameter.

The output is created from an ordered hash table. The Level00 property is set to the full path as input.

Set the counter and split the path to get the parent.

Use a while loop to add the current path into the hash table – create the property name with the string format operator –f.

Increment the counter and split the path – again taking the parent.

The while loop runs while the path has data. If you try to split when the path just contains the drive you get an empty string

PS> (Split-Path -Path c:\ -Parent) -eq ''
True

 

The data is output as an object so you can access a particular level. For instance if you want the great-grandfather level – you use Level03

PS> $test.Level03
C:\Scripts

 

You can use the output object to create a new path based on the level of your choice

PS> Join-Path -Path $test.Level03 -ChildPath Test47
C:\Scripts\Test47


September 19, 2018  5:07 AM

PowerShell v6.1

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

PowerShell v6.1 was released last week – there’s no big ticket items like v6.0 but a lot of bug fixes and minor improvements.

You can download from https://github.com/PowerShell/PowerShell/releases

and find the release notes at

https://docs.microsoft.com/en-us/powershell/scripting/whats-new/what-s-new-in-powershell-core-61?view=powershell-6


September 14, 2018  9:10 AM

Get-Date – DisplayHint

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

On the surface Get-Date is a simple cmdlet that’s been around since PowerShell v1. However, it has some interesting quirks. In this post I’ll show how Get-Date –DisplayHint works.

By default Get-Date returns the data and time

PS> Get-Date

13 September 2018 16:09:34

The DisplayHint parameter ( available in PowerShell v5.1, 6.0 and 6.1) modifies the data shown

PS> Get-Date -DisplayHint Date

13 September 2018

PS> Get-Date -DisplayHint DateTime

13 September 2018 16:11:18

PS> Get-Date -DisplayHint Time

16:11:25

You might think that all –DisplayHint does is just force the display to be date, time or datetime and you’ve lost the rest of the data but in reality all that’s happening is that the default display of your object is constrained by the value of the DisplayHint property:

PS> Get-Date | Get-Member -Name DisplayHint

TypeName: System.DateTime

Name MemberType Definition
—- ———- ———-
DisplayHint NoteProperty DisplayHintType DisplayHint=DateTime
PS> Get-Date -DisplayHint Date | Get-Member -Name DisplayHint

TypeName: System.DateTime
Name MemberType Definition
—- ———- ———-
DisplayHint NoteProperty DisplayHintType DisplayHint=Date

PS> Get-Date -DisplayHint Time | Get-Member -Name DisplayHint

TypeName: System.DateTime
Name MemberType Definition
—- ———- ———-
DisplayHint NoteProperty DisplayHintType DisplayHint=Time

Note that DateTime is the default setting if –DisplayHint isn’t used.

PS> $t = Get-Date -DisplayHint Time
PS> $t
16:16:59

But you can still access the other properties of the datetime object returned by get-date

PS> $t.Date
13 September 2018 00:00:00

PS> $t.Year
2018
PS> $t.Hour
16

If you just need to see the date or time –DisplayHint is a good place to start


September 13, 2018  9:43 AM

PowerShell new line

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

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 overwritten

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 recognized:

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 characters with a maximum value of 10FFFF


September 8, 2018  6:15 AM

PowerShell check file exists

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

There are times when you need to check if a file exists – this is how you do a PowerShell check file exists.

If you try to access a file that doesn’t exist you’ll get an error:

PS> Get-Content -Path c:\test\z27.txt
Get-Content : Cannot find path ‘C:\test\z27.txt’ because it does not exist.
At line:1 char:1
+ Get-Content -Path c:\test\z27.txt
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : ObjectNotFound: (C:\test\z27.txt:String) [Get-Content], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetContentCommand

Rather than manage the error directly you can test to see if the file exists

PS> Test-Path -Path c:\test\z27.txt
False

Test-Path returns a boolean (true or false) so you can then test the file exists before accessing

PS> if (Test-Path -Path c:\test\z27.txt) {Get-Content -Path c:\test\z27.txt}

You’d obviously add an else block to deal with the case of the file not existing – possibly just a warning or a throw depending on what you were doing,

You can also test if a folder exists

PS> Test-Path -Path c:\test\
True
PS> Test-Path -Path c:\test27\
False

You can be more exact with whether you’re dealing with a folder or a file by using the –PathType parameter

PS> Test-Path -Path c:\test -PathType Any
True
PS> Test-Path -Path c:\test -PathType Container
True
PS> Test-Path -Path c:\test -PathType Leaf
False
PS> Test-Path -Path C:\test\p1.txt -PathType Container
False
PS> Test-Path -Path C:\test\p1.txt -PathType Leaf
True

Use container for a folder and leaf for a file.

Alternatively you can use –Filter, –Include and –Exclude but remember you’ll only get a boolean returned so

PS> Test-Path -Path C:\test\* -Filter *.txt
True

gives a single return even though there are 20 .txt files in the folder!

Your last alternative is to –OlderThan or –NewerThan to test against a files age

PS> Test-Path C:\test\* -Filter *.txt -NewerThan ((Get-Date).AddDays(-3))
False
PS> Test-Path C:\test\* -Filter *.txt -OlderThan ((Get-Date).AddDays(-7))
True

I do have .txt files older than 7 days but none younger than 3 days old.

The –LiteralPath parameter  means that the path is used exactly as given and ignores any possible interpretation of a character as anything else. use it instead of –Path.

Finally –IsValid tests the validity of the path syntax even if the path doesn’t exist so

PS> Test-Path -Path C:\test\z27.txt
False
PS> Test-Path -Path C:\test\z27.txt -IsValid
True

The file itself doesn’t exist but the path is valid.  Conversely

PS> Test-Path -Path C:\test\p1.txt
True
PS> Test-Path -Path C:\test\p1.txt -IsValid
True
PS> Test-Path -Path C:\test\p?1.txt -IsValid
False
PS> Test-Path -LiteralPath C:\test\p?1.txt -IsValid
False

p1.txt exists and has a valid path but p?1.txt isn’t a valid path.

On Windows folder and file names are case INSENSITIVE so

PS> Test-Path -Path C:\test\p1.txt
True
PS> Test-Path -Path C:\tEsT\p1.txt
True

both work.

On Linux folder and file names are case sensitive so be aware. How to deal with this situation is currently under discussion

https://github.com/PowerShell/PowerShell/issues/7578


September 6, 2018  11:13 AM

Hyper-V book soon available

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Hyper-V, Powershell

I’ve been working with Andy Syrewicze on a Hyper-V book that’ll be available in the next few months.

https://www.apress.com/gb/book/9781484241158

https://www.amazon.co.uk/Pro-Microsoft-Hyper-V-2019-Hands/dp/1484241150/ref=sr_1_1?s=books&ie=UTF8&qid=1536253236&sr=1-1&keywords=Hyper-V+siddaway

https://www.amazon.com/Pro-Microsoft-Hyper-V-2019-Hands/dp/1484241150/ref=sr_1_1?ie=UTF8&qid=1536253327&sr=8-1&keywords=hyper-v+siddaway

Its’s a rewrite and update of the Month of Lunches book I was working on a while back.

Pleased with the way it turned out and hope you’ll find it useful


August 31, 2018  6:05 AM

PowerShell string contains

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

How can you check if a PowerShell string contains a character or substring?

You might be tempted to try this:

PS> $s = ‘abcdefghijk’
PS> $s -contains ‘f’
False

But –contains is for working with the contents of arrays. So you could do this:

PS> ($s.ToCharArray()) -contains ‘f’
True

You’re implicitly converting the string ‘f’ to [char] to make the comparison. Your comparison is actually this

PS> ($s.ToCharArray()) -contains [char]’f’
True

That’s fine for a single character but if you want to test a substring

PS> $s -contains ‘def’
False
PS> ($s.ToCharArray()) -contains ‘def’
False

That approach won’t work.

You need to use the Indexof method

PS> $s.Indexof(‘f’)
5
PS> $s.Indexof(‘def’)
3

The value returned is the position of the FIRST character in the substring.

You can also test an an array of characters

PS> $a = ‘g’,’j’,’a’
PS> $s.IndexOfAny($a)
0

Again you get the FIRST character in this case the ‘a’

Remember PowerShell is .NET based so the first index is 0

Lets get some repetition into our target

PS> $s = $s * 3

PS> $s
abcdefghijkabcdefghijkabcdefghijk

You also have the option of picking the LAST occurrence of the substring

PS> $s.LastIndexOf(‘f’)
27

PS> $s.LastIndexOfAny($a)
31

This last one is the last ‘j’ in the string – its the last occurrence of any of the characters you wanted to match.

If there isn’t a match you get –1 returned

PS> $s.IndexOf(‘z’)
-1
PS> $s.LastIndexOf(‘z’)
-1


August 28, 2018  4:42 AM

PowerShell Day UK Agenda Update

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

There’s been a Powershell Day UK Agenda Update.

Looks like there’ll be three tracks for most of the day.

Full agenda and tickets from – https://psday.uk/


Forgot Password

No problem! Submit your e-mail address below. We'll send you an e-mail containing your password.

Your password has been sent to: