Monthly Archive

Categories

Monthly Archives: October 2018

Find a missing integer–alternative solution

In my recent post about finding a missing integer in an array of numbers I didn’t spell out the assumptions. In this post I’ll explain those assumptions and show you how to Find a missing integer–alternative solution.

The assumptions were:

The sequence of numbers in the array starts at 1

The values increment by 1

 

If the sequence doesn’t start at 1 the technique fails as the expected sum being n(n+1)/2 breaks down.

As an alternative you can calculate the expected sum by finding the average of the numbers in the sequence and multiplying by the number of expected entries in the sequence.

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

[int]$expectedlength
)

#$expectedSum = ($expectedlength * ($expectedlength + 1) ) / 2

# don't know if array is in numeric order
$sarray = $iarray | sort

$expectedSum = $expectedlength * (($sarray[0] + $sarray[-1]) / 2)

$actualsum = 0
foreach ($n in $iarray) {
$actualsum += $n
}

$missingnumber = $expectedSum - $actualsum
$missingnumber
}

 

I’ve changed the function name. The only other change is to sort the array and calculate the expected sum using the average

$sarray = $iarray | sort
$expectedSum = $expectedlength * (($sarray[0] + $sarray[-1]) / 2)

 

The examples from the previous post work as does

PS> $iarray = 6,7,8,9,10,11,12,13,14,15,16,17,18,20,21,22,23,24,25

get-missinginteger -iarray $iarray -expectedlength 20

19

Finding and removing duplicates

Continuing the thoughts about techniques for dealing with integer arrays – this is how to go about finding and removing duplicates from the array.

First – to find any duplicates use Group-Object

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

$iarray | Group-Object |
where Count -gt 1 |
foreach {
New-Object -TypeName PSobject -Property @{
DuplicateNumber = $_.Name
Count = $_.Count
}
}
}

 

If the count is greater than 1 it means you have duplicates so create an output object and display the integer value and the total number of occurrences.

 

Removing duplicates is a bit more tricky

function remove-duplicate {
[CmdletBinding()]
param (
[int[]]$iarray
)

$dupcount =@{}

$duplicates = get-duplicate -iarray $iarray

$dedup = $iarray | foreach {
if ($_ -in $duplicates.DuplicateNumber) {
if ( -not $dupcount["$_"]) {
$dupcount += @{"$_" = 1 }
$psitem
}
}
else {
$psitem
}
}

$dedup
}

 

Get the duplicates as before. Iterate over the input array. If the value is found in $duplicates.DuplicateNumber – your using the object property as a property of the array to effectively get an array of the duplicate values – test to see if its in the $dupcount hashtable. If it isn’t add an entry to the hashtable and output the value. If it is in $dupcount do nothing and therefore skip that value.

 

If the value isn’t in $duplicates.DuplicateNumber emit the value.

 

You’ll notice that any values output by the foreach cmdlet go straight into $dedup array – this builds that array automatically and is the most efficient way to get the values into the array.

 

Final step is to output the de-duplicated array.

Find a missing integer

I stumbled across a set of programmer interview questions recently. They raised some interesting problems that I thought would be fun to show a PowerShell answer. This is the first of them – find a missing integer in an array.

Consider an array of integers. It should have 10 members:

$iarray = 1,2,3,4,5,6,7,8,9,10

 

But what if one is missing

$iarray = 1,2,3,4,5,6,8,9,10

In this case the number 7 is missing.

 

This problem assumes that there is a regular increment between members of the array – in this case 1. In mathematical terms its a series.

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

[int]$expectedlength
)

$expectedSum = ($expectedlength * ($expectedlength + 1) ) / 2

$actualsum = 0
foreach ($n in $iarray) {
$actualsum += $n
}

$missingnumber = $expectedSum - $actualsum
$missingnumber
}

 

The solution is to find the expected sum of the series which is found using

(n * (n + 1)) / 2

where n is the expected length of the series. 1 is the increment between members of the series. You can generalise this for any increment but that’s left as an exercise for you.

 

Then calculate the actual sum by looping through the array and summing the values.

 

The missing number is the difference between the expected sum and the actual sum

 

Here’s some examples

PS> ## 10 integers - number 7 missing
PS> $iarray = 1,2,3,4,5,6,8,9,10
PS> get-missingnumber -iarray $iarray -expectedlength 10
7

PS> ## 25 integers - number 19 missing
PS> $iarray = 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,20,21,22,23,24,25

PS> get-missingnumber -iarray $iarray -expectedlength 25
19

 

A quick way to generate the array to play with:

PS> $r = Get-Random -Maximum 26 -Minimum 1
PS> $iarray = 1..25 | where {$psitem -ne $r}
PS> get-missingnumber -iarray $iarray -expectedlength 25
22
PS> $r
22

Managing errors

If you try to do a directory listing through C:\Windows\system32 you’ll get some access denied errors. I’m going to show you how to do this while managing errors that arise. The code will also highlight a few of the PowerShell error handling techniques.

 

$epath2 = $null

Get-ChildItem -Path C:\Windows\System32 -Recurse -Force -Directory -ErrorVariable epath2 |
foreach {
if ($?) {
Out-File -InputObject $_.FullName -FilePath c:\test\paths.txt -Append
}
}

if ($epath2) {
$epath2 |
foreach {
($_.Exception -split "'")[1] | Out-File -FilePath c:\test\errors.txt -Append
}
}

 

Set $epath2 to $null to clear any data.

 

Use -ErrorVariable epath2 to define the variable into which errors are directed.

 

Get-ChildItem will recurse through the folder structure – use –Directory to ignore files.

 

In the foreach statement check $? which holds True or False depending on if last statement succeeded. Append the full path to the folder to the paths.txt file.

 

If $epath2 has a value then iterate over the values splitting out the folder paths from the error statement. Write to errors.txt

Test-Connection cmdlet

The Test-Connection cmdlet wasn’t included in PowerShell v6.0 but did make a come back in v6.1.

 

The v6.1 version of Test-Connection has some serious issues as I’ve described before.

 

Work is being done at the moment to remedy those issues – hopefully for v6.2

 

This is your chance to comment on a cmdlet and help determine how it will work.

 

The issues to comment on are:

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

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

 

PowerShell v6 is community driven.  This is an opportunity to help drive the PowerShell you want to see.

DevOps team structures

DevOps has never really excited me and I never understood why until I read about DevOps team structures - https://web.devopstopologies.com/

The article suggests 7 bad practices or ‘anti-types:

  1. Dev and Ops Silos
  2. DevOps Team Silo
  3. Dev doesn’t need Ops
  4. DevOps as tools team
  5. Rebranded administrators
  6. Ops embedded in dev team
  7. Dev and DBA silos

 

It then looks at some team structures that can be made to work

  1. Dev and Ops collaboration
  2. Fully Shared Ops responsibilities
  3. Ops as Infrastructure as a Service
  4. DevOps as external service
  5. DevOps team with expiry date
  6. DevOps evangelist team
  7. Site Reliability Engineering team
  8. Container driven collaboration
  9. Dev and DBA collaboration

 

One thing that’s often not explicitly mentioned when discussing DevOps is the need for whatever teams, and individuals, to communicate. This is often anathema in my experience and why a DevOps type approach is so badly needed.

 

As to why DevOps hasn’t excited me – the article made me realise I’d spent a good part of my working life moving between Dev & Ops and often occupying the middle, grey area that DevOps makes its own. Its what I’ve been doing for a long time mainly without the tools we have today and not viewing it as anything special – just getting the job done.

PowerShell v6.2 preview 1

PowerShell v6.2 preview 1 quietly appeared yesterday - https://github.com/PowerShell/PowerShell/releases

No major breaking changes.

 

New features include:

  • Type accelerators for unsigned integers
  • Support for cd+
  • Test-ModuleManifest update to correctly populate nested modules
  • Set-Service –status stopped will also stop dependencies

 

The Test-Connection and –workingdirectory issues are still unresolved.

 

There’s nothing earth shattering in the updates but that seems to be the name of the game with PowerShell v6 – lots of small incremental changes

PowerShell v6.1 working directory

PowerShell v6.1 introduced a very annoying change in that a –workingdirectory parameter was added to pwsh.exe. When you install v6.1 working directory is automatically set to ~ which is your home folder. The workingdirectory doesn’t play nice with your profile. In my profile I set the location to C:\scripts where I keep my work in progress and scripts I use on a frequent basis. If –workingdirectory is set to ~ my profile is ignored depending on whether I’m running as administrator (profile is honoured) or not. This is how I got round the annoyance of the PowerShell v6.1 working directory.

 

This is for WINDOWS only. If you’re on Linux or mac you’ll need to adapt this as appropriate.

 

First option is to go in and edit the icon – Right click the PowerShell icon on start menu. Select More –> Open file location. Select the PowerShell shortcut, right click and select Properties. Edit the working directory parameter. Either remove is and the folder or change the folder.

 

So that’s manual and painful. Unfortunately, there isn’t any functionality in PowerShell to manage short cuts. There is a way using COM objects.

function get-icon {

$wsshell = New-Object -ComObject 'WScript.Shell'

$icon = $wsshell.CreateShortcut('C:\ProgramData\Microsoft\Windows\Start Menu\Programs\PowerShell\PowerShell 6 (x64).lnk')

$icon

}

 

Create an instance of the WScript.Shell COM object. Notice you have to use the CreateShortcut method to get the shortcut properties!

 

The important line in the output is

Arguments : -WorkingDirectory C:\Scripts

 

You can clear the arguments

function clear-argument {

$wsshell = New-Object -ComObject 'WScript.Shell'

$icon = $wsshell.CreateShortcut('C:\ProgramData\Microsoft\Windows\Start Menu\Programs\PowerShell\PowerShell 6 (x64).lnk')

$icon.Arguments = ''

$icon.Save()

}

 

Ideally, I suppose you should just remove the –workingdirectory argument but quick and dirty works for me on this.

 

If you don’t have a –workingdirectory parameter set your profile isn’t honoured with regard to setting locations unless you run as administrator.

To set the working directory

function set-argument {

$wsshell = New-Object -ComObject 'WScript.Shell'

$icon = $wsshell.CreateShortcut('C:\ProgramData\Microsoft\Windows\Start Menu\Programs\PowerShell\PowerShell 6 (x64).lnk')

$icon.Arguments = '-WorkingDirectory C:\Scripts'

$icon.Save()

}

 

If you change the argument in this manner you may need to recreate any icons you pinned to the start bar.

PowerShell approved verbs

PowerShell uses a verb-noun style for naming cmdlets. The list of Powershell approved verbs is available at https://docs.microsoft.com/en-us/powershell/developer/cmdlet/approved-verbs-for-windows-powershell-commands.

 

You should use the approved verbs when writing functions or cmdlets. Import-Module will complain about using unapproved verbs.

 

You can also see the list of approved verbs using Get-Verb.

 

There’s also a set of verb naming rules that are meant for you to follow. Some are good ideas like not using the reserved verbs – others such as dictating the use of Pascal syntax I ignore because I prefer to use lower case for function names. It helps me remember which is my code! If I was publishing to the gallery I’d probably conform but as I tend to write proof of concept code more than anything I want to keep a visual separation.

 

The other very useful set information are the synonyms for verbs that you shouldn’t use. For instance don’t use Append, Attach, Concatenate or Insert – use Add. Some of this information is contextual though as you shouldn’t use Pop or Out as a synonym for Exit BUT Pop is perfectly valid when removing an item off a stack (Pop-Location is the only cmdlet I know of that works in that way).

Running Ping tests

I’ve shown how to discover network information using the get-internetconnection function and how to construct simple Pester tests to ping the IP addresses returned by get-internetconnection. This time I’ll show you how to create a control function for running ping tests.

In fact the control script could be used to run any set of tests that you need.

 

function invoke-troubleshooting {
[CmdletBinding()]
param (
[ValidateSet('Ping', 'Remoting')]
[string]$testTorun
)

switch ($testTorun) {
'Ping' { $path = 'C:\Scripts\TroubleShooting\PingTests.ps1'
         $netinfo = get-internetconnection
}
'Remoting' {
         $path = 'C:\Scripts\TroubleShooting\RemotingTests.ps1'
}
}

$tests = Get-Content $path | 
Select-String -Pattern 'Describe' | 
foreach {
  (($_ -split 'Describe')[1]).Trim('{').Trim().Trim("'") 
}

$data = foreach ($test in $tests) {

$result = $null
$result = Invoke-Pester -Script @{Path = "$path"} -PassThru -TestName "$test" -Show None

$props = [ordered]@{
'Test' = $result.TestResult.Name
'Result' = $result.TestResult.Result
'Failure Message' = $result.TestResult.FailureMessage
}
New-Object -TypeName PSObject -Property $props

if ($result.FailedCount -gt 0) {break}
}

$data
}

 

The function invoke-troubleshooting takes a single parameter that controls the tests to be run.

 

A switch statement sets the path to the relevant file and gathers any needed data. The file containing the tests is read to discover the names of the tests. I could probably make the clean up of the test name a bit neater but it works.

 

Each test in the list of tests is run – with no output. An object with the test results is created and output – all objects collected in the $data variable.

 

If a test fails processing stops and the results are returned. To ensure that the results aren’t truncated use Format-Table:

 

PS> invoke-troubleshooting -testTorun Ping | Format-Table -AutoSize -Wrap

Test                                Result  Failure Message 
----                                ------  --------------- 
Loopback should be available        Passed 
Local adapter should be available   Passed 
Default Gateway should be available Passed 
DNS server should be available      Passed 
Target server should be available   Failed  Expected: {True} 
                                            But was: {False}

 

You can immediately see what passed and failed to zoom in on the potential problem. Remember there could be more than one problem and you need to iterate through the tests until they are all successful.

 

In case you remember that Windows 7 introduced a TroubleshootingPack module with Windows 7 I’m not re-inventing the wheel. That module has a limited number of options – see C:\Windows\diagnostics\system – and is more concerned with troubleshooting an individual machine. The framework I’ve outlined in these three articles could be used to test any aspect of YOUR environment – all you need to add is the file with the tests and functions to gather any required data. Don’t forget to add the test to the list of options on $testTorun!