Monday, August 24, 2015

Finding Drive Letters

Here is a simple function to find out the reserved drive letters:

#requires -Version 3

function Get-DriveLetter
    (Get-WmiObject -Class Win32_LogicalDisk).DeviceID

To list all drive letters in use, try this:

PS> Get-DriveLetter


To find out whether a given drive letter is reserved, you could use it like this:

PS> $letters = Get-DriveLetter

PS> $letters -contains 'c:'

PS> $letters -contains 'f:'


Twitter This Tip! ReTweet this Tip!

Friday, August 21, 2015

Quickly Setting Multiple Environment Variables

To quickly (and permanently) set a bunch of environment variables, here is a nice approach:

$hashtable = @{
    Name = 'Weltner'
    ID = 12
    Ort = 'Hannover'
    Type = 'Notebook'
    ABC = 123

$hashtable.Keys | ForEach-Object {
    $Name = $_
    $Value = $hashtable.$Name
    [Environment]::SetEnvironmentVariable($Name, $Value, "User")


Simply define the variables in a hash table. The script will create one environment variable per key-value pair. Replace "User" by "Machine" to create machine-wide environment variables. This would require Administrator privileges, though.

With the very same approach, you can also delete environment variables. Simply assign an empty string to it inside the hash table.

Twitter This Tip! ReTweet this Tip!

Thursday, August 20, 2015

Quickly Finding Scripts

To quickly locate a PowerShell script anywhere in your MyDocuments folder, take a look at this Find-Script function:

#requires -Version 3
function Find-Script
    [Parameter(Mandatory = $true)]
    $Path = [Environment]::GetFolderPath('MyDocuments')

  Get-ChildItem -Path $Path  -Filter *.ps1 -Recurse -ErrorAction SilentlyContinue |
  Select-String -Pattern $SearchPhrase -List |
  Select-Object -Property Path, Line |
  Out-GridView -Title "All Scripts containing $SearchPhrase" -PassThru |
  ForEach-Object -Process {
    ise $_.Path

Run it like this:

Find-Script 'childitem' 

This would return a list of all PowerShell scripts in your user profile that contain the search word. When you select scripts in a grid view window and click OK, they are automatically opened in the PowerShell ISE.

To set a different search base, use the –Path parameter. This way, you can search your USB media or some network location just as easily.

Twitter This Tip! ReTweet this Tip!

Tuesday, August 18, 2015

Adding Additional Information to Objects

When you retrieve results, you may want to add additional properties to the results so later you know where they came from.

Attaching additional information to complex objects works a little different than attaching them to primitive data (as described in an earlier tip).

Get-Process | 
  Add-Member -MemberType NoteProperty -Name PC -Value $env:COMPUTERNAME -PassThru |
  Select-Object -Property Name, Company, Description, PC |

Here, the processes returned by Get-Process get an additional property called "PC" which contains the computer name where they were taken.

To view custom properties, make sure you either use Select-Object and specify the property name, or use the dot notation:

$list = Get-Process | 
  Add-Member -MemberType NoteProperty -Name PC -Value $env:COMPUTERNAME -PassThru
$list | ForEach-Object { 'Process {0} on {1}' -f $_.Name, $_.PC }

Twitter This Tip! ReTweet this Tip!

Monday, August 17, 2015

Appending Extra Information to Primitive Data Types

Maybe you'd like to tag a variable and provide some extra information. In PowerShell, use Add-Member and attach NoteProperties or ScriptProperties to it.

A NoteProperty contains some static information, whereas a ScriptProperty runs code when you retrieve the property.

Have a look how that would work with a simple string:

$a = "some text"

$a = $a | Add-Member -MemberType NoteProperty -Name Origin -Value $env:computername -PassThru
$a = $a | Add-Member -MemberType ScriptProperty -Name Time -Value { Get-Date } -PassThru


Twitter This Tip! ReTweet this Tip!

Friday, August 14, 2015

Simple Replacement for INI Files

If you'd like to keep settings outside of your script and store them in a separate config file, then you can use all kinds of data formats for it.

INI files lack native support so you would have to parse them manually. JSON and XML have parsers but produce too complex text that is not easily maintained by mortals.

If your config data can be expressed as key-value pairs like below, then we have an alternative:

Name = 'Tom'
ID = 12
Path = 'C:'

Save the key-value pairs to a plain text file, then use this code to read the file:

$hashtable = @{}
$path = 'z:\yourfilename.config'

$payload = Get-Content -Path $path |
Where-Object { $_ -like '*=*' } |
ForEach-Object {
    $infos = $_ -split '='
    $key = $infos[0].Trim()
    $value = $infos[1].Trim()
    $hashtable.$key = $value

The result is a hash table, and you can easily access the values like this:


Twitter This Tip! ReTweet this Tip!

Thursday, August 13, 2015

Remove Array Elements

Did you ever need to compare two arrays? Compare-Object might help. Check this out:

$array1 = 1..100
$array2 = 2,4,80,98

Compare-Object -ReferenceObject $array1 -DifferenceObject $array2 | 
  Select-Object -ExpandProperty InputObject

The result is the content of $array1 minus the content of $array2.

To find out the array elements that are missing in $array1 but are present in $array2, try this:

$array1 = 1..100
$array2 = 2,4,80,98, 112

Compare-Object -ReferenceObject $array1 -DifferenceObject $array2 -ExcludeDifferent -IncludeEqual | 
  Select-Object -ExpandProperty InputObject

Twitter This Tip! ReTweet this Tip!

Wednesday, August 12, 2015

Quickly Getting IP Addresses

You want to quickly get a list of IP addresses for your own computer or a network machine? Here is how:

#requires -Version 3

$ComputerName = ''


To only get IPv4, try this:

#requires -Version 1

$ComputerName = ''

[System.Net.Dns]::GetHostAddresses($ComputerName) |
Where-Object {
  $_.AddressFamily -eq 'InterNetwork' 
} |
Select-Object -ExpandProperty IPAddressToString

Likewise, to get IPv6, adjust to this:

#requires -Version 1

$ComputerName = ''

[System.Net.Dns]::GetHostAddresses($ComputerName) |
Where-Object {
  $_.AddressFamily -eq 'InterNetworkV6' 
} |
Select-Object -ExpandProperty IPAddressToString

Twitter This Tip! ReTweet this Tip!

Tuesday, August 11, 2015

Shortening Text

Let's assume you want to chop off some text at the end of a string. This is the traditional approach using string operations:

$text = "Some text"
$fromRight = 3

$text.Substring(0, $text.Length - $fromRight)

A much more powerful way uses the -replace operator with regular expressions:

$text = "Some text"
$fromRight = 3

$text -replace ".{$fromRight}$"

This would take any character (".") that is $fromRight characters long and ends with the end of text ($).

Since regular expressions are really flexible, you could rewrite it to chop off digits only, and only a maximum of 4:

$text1 = "Some text with digits267686783"
$text2 = "Some text with digits3"

$text1 -replace "\d{0,5}$"
$text2 -replace "\d{0,5}$"

The quantifier "{0,5}" tells the regex engine that you want between 0 and 5 numbers, and the engine will take as much as it can get.

Twitter This Tip! ReTweet this Tip!

Monday, August 10, 2015

Avoid Using Redirection

While you can still use the old redirection operator to write command output to a file, you should rather use PowerShell cmdlets instead. Here is why:

#requires -Version 2

$OutPath = "$env:temp\report.txt"

Get-EventLog -LogName System -EntryType Error, Warning -Newest 10 > $OutPath

notepad.exe $OutPath 

This produces a text file that exactly represents what else would have been displayed in the console, not taking into consideration the flexible object nature.

The next example makes sure no part of the output text is truncated, and the output uses UTF8 encoding--all options that you don't have with simple redirection:

#requires -Version 2

$OutPath = "$env:temp\report.txt"

Get-EventLog -LogName System -EntryType Error, Warning -Newest 10 |
Format-Table -AutoSize -Wrap |
Out-File -FilePath $OutPath -Width 100

notepad.exe $OutPath

Twitter This Tip! ReTweet this Tip!