bookmark_borderPowershell Variable Scoping

Confusion caused by variable scoping is a constant source of errors in Powershell. Most importantly, when is a variable visible, but any changes aren’t visible outside of the current scope. This is especially common with inner functions.

Here is an example, declaring a variable in an outer function makes it visible to the inner function. But it is passed by copy, so updating it in the inner function does not make changes visible to the outer function.

function Outer
{
    $var1 = 1;
    
    function Inner
    {
        Write-Output "Inner: $var1"

        # This change will not be visible in the outer function
        $var1++;
        Write-Output "Inner: $var1"
    }

    Write-Output "Outer: $var1"
    Inner
    Write-Output "Outer: $var1"
}
Outer

# Output: 
# Outer: 1
# Inner: 1
# Inner: 2
# Outer: 1

One way to fix that, is to declare the variable either “AllScope”, or in the global namespace. “AllScope” would be more advisable, since AllScope variables are available in all child scopes, as opposed to everywhere.

function Outer
{
    $var1 = 1
    Set-Variable -Name allscope -Value 1 -Option AllScope
    $global:var3 = 1
    
    function Inner
    {
        Write-Output "Inner: var1:$var1 allscope:$allscope var3:$global:var3"

        # This change will not be visible in the outer function
        $var1++;
        $allscope++;
        $global:var3++;
        Write-Output "Inner: var1:$var1 allscope:$allscope var3:$global:var3"
    }

    Write-Output "Outer: var1:$var1 allscope:$allscope var3:$global:var3"
    Inner
    Write-Output "Outer: var:$var1 allscope:$allscope var3:$global:var3"
}
Outer

# Outputs:
# Outer: var1:1 allscope:1 var3:1
# Inner: var1:1 allscope:1 var3:1
# Inner: var1:2 allscope:2 var3:2
# Outer: var:1 allscope:2 var3:2

Here is a program which executes to show a bunch of other behaviors when it commends to appending values to an array.

$Global:GlobalLevelArray = @("Script!");
$ScriptLevelArray = @("Script!");

function OuterFunc
{
    param
    (
        $OuterFuncArrayArg
    )

    process
    {
        # Changes to global variables will always be visible
        $Global:GlobalLevelArray = $Global:GlobalLevelArray + "OuterFunc";

        # Notice that this will update the array at this scope,
        # but not outside of this function.
        $ScriptLevelArray = $ScriptLevelArray + "OuterFunc";

        # This will update the array at this scope,
        # but changes will not be accessible to the caller.
        $OuterFuncArrayArg = $OuterFuncArrayArg + "OuterFunc";

        # This variable is visible at this level, and to child functions.
        $OuterFuncArray = @("OuterFunc");

        # A variable at the local scope is the current scope, but will not be available to the child scope.
        $local:OuterFuncLocalArray = @("OuterFunc");

        # This variable is available an modifieable to all child functions
        # Changes by the child will be reflected in here.
        Set-Variable -Name OuterFuncAllScopes -Value @("OuterFunc") -Option AllScope

        function NestedFunc
        {
            param
            (
            )
            process
            {
                $Global:GlobalLevelArray = $Global:GlobalLevelArray + "NestedFunc";
                $ScriptLevelArray = $ScriptLevelArray + "NestedFunc";
                $OuterFuncArrayArg = $OuterFuncArrayArg + "NestedFunc";
                $OuterFuncArray = $OuterFuncArray + "NestedFunc";
                $local:OuterFuncLocalArray = $local:OuterFuncLocalArray + "NestedFunc";
                $OuterFuncAllScopes = $OuterFuncAllScopes + "NestedFunc";

                # This, even though its declared all the way down in this nested func, will be available everywhere.
                $global:NestedFuncGlobalArray = @("NestedFunc")

                Write-Output "`t`tNestedFunc:GlobalLevelArray -> $($Global:GlobalLevelArray -join ',')"
                Write-Output "`t`tNestedFunc:ScriptLevelArray -> $($ScriptLevelArray -join ',')"
                Write-Output "`t`tNestedFunc:OuterFuncArrayArg -> $($OuterFuncArrayArg -join ',')"
                Write-Output "`t`tNestedFunc:OuterFuncArray -> $($OuterFuncArray -join ',')"
                Write-Output "`t`tNestedFunc:OuterFuncLocalArray -> $($local:OuterFuncLocalArray -join ',')"
                Write-Output "`t`tNestedFunc:OuterFuncAllScopes -> $($OuterFuncAllScopes -join ',')"
                Write-Output "`t`tNestedFunc:NestedFuncGlobalArray -> $($global:NestedFuncGlobalArray -join ',')"
            }
        }

        NestedFunc

        $global:NestedFuncGlobalArray = $global:NestedFuncGlobalArray + "OuterFunc";

        Write-Output "`tOuterFunc:ScriptLevelArray -> $($ScriptLevelArray -join ',')"
        Write-Output "`tOuterFunc:OuterFuncArrayArg -> $($OuterFuncArrayArg -join ',')"
        Write-Output "`tOuterFunc:GlobalLevelArray -> $($Global:GlobalLevelArray -join ',')"
        Write-Output "`tOuterFunc:OuterFuncArray -> $($OuterFuncArray -join ',')"
        Write-Output "`tOuterFunc:OuterFuncLocalArray -> $($local:OuterFuncArray -join ',')"
        Write-Output "`tOuterFunc:OuterFuncAllScopes -> $($OuterFuncAllScopes -join ',')"
        Write-Output "`tOuterFunc:NestedFuncGlobalArray -> $($global:NestedFuncGlobalArray -join ',')"
    }
}


OuterFunc -OuterFuncArray $ScriptLevelArray

$global:NestedFuncGlobalArray = $global:NestedFuncGlobalArray + "Script!";


Write-Output "Script:ScriptLevelArray -> $($ScriptLevelArray -join ',')"
Write-Output "Script:GlobalLevelArray -> $($GlobalLevelArray -join ',')"

Write-Output "Script:OuterFuncLocalArray -> $($local:OuterFuncArray -join ',')"

# An AllScopes variable in a child scope will not be visible in the parent scope
Write-Output "Script:OuterFuncAllScopes -> $($OuterFuncAllScopes -join ',')"
Write-Output "Script:NestedFuncGlobalArray -> $($global:NestedFuncGlobalArray -join ',')"

The output of this progrom:

		NestedFunc:GlobalLevelArray -> Script!,OuterFunc,NestedFunc
		NestedFunc:ScriptLevelArray -> Script!,OuterFunc,NestedFunc
		NestedFunc:OuterFuncArrayArg -> Script!,OuterFunc,NestedFunc
		NestedFunc:OuterFuncArray -> OuterFunc,NestedFunc
		NestedFunc:OuterFuncLocalArray -> NestedFunc
		NestedFunc:OuterFuncAllScopes -> OuterFunc,NestedFunc
		NestedFunc:NestedFuncGlobalArray -> NestedFunc
	OuterFunc:ScriptLevelArray -> Script!,OuterFunc
	OuterFunc:OuterFuncArrayArg -> Script!,OuterFunc
	OuterFunc:GlobalLevelArray -> Script!,OuterFunc,NestedFunc
	OuterFunc:OuterFuncArray -> OuterFunc
	OuterFunc:OuterFuncLocalArray -> OuterFunc
	OuterFunc:OuterFuncAllScopes -> OuterFunc,NestedFunc
	OuterFunc:NestedFuncGlobalArray -> NestedFunc,OuterFunc
Script:ScriptLevelArray -> Script!
Script:GlobalLevelArray -> Script!,OuterFunc,NestedFunc
Script:OuterFuncLocalArray -> 
Script:OuterFuncAllScopes -> 
Script:NestedFuncGlobalArray -> NestedFunc,OuterFunc,Script!

bookmark_borderPowershell Postfix to Infix

Here is a powershell example of converting an expression in postfix notation into infix notation. A little background on postfix notation, is that the operators follow the operands. For example “(1 + 2) = 1 2 +”. Postfix is much easier to interpret for a computer, since values are processed as they are used, and their is no need for parenthesis.

#
# Here is an example. 
# Postfix String: 1 2 - 3 * 4 * 5 - = -17
# Infix String:   ((((1 - 2) * 3) * 4) - 5) = -17
#

Here is an example of the algorithm followed by this example.

#
# Algorithm: For every item 'x' in the string
#    x -> number: push it onto the stack
#    x -> operator: pop two items of the stack, join them with x,
#                   then push the result back onto the stack.
#                   EX: "(stack.pop() x stack.pop())"
#
# Given the string "1 2 - 3 * 4 * 5 -"
#
# We use a stack to keep track of our progress.
#
# First item in the string = '1'
# '1' is a number so push it onto the stack
#
#  stack                  remaining: 2 - 3 * 4 * 5 -
#    1
#
# Next item in the string = '2'
# '2' is a number so push it onto the stack
#
#  stack                  remaining: - 3 * 4 * 5 -
#    2
#    1
#
# Next item in the string = '-'
# '-' is an operator, so pop two items of the stack, join, then push
#
#  stack                  remaining: 3 * 4 * 5 -
# (1 + 2)
#
# Next item in the string = '3'
# '3' is a number so push it onto the stack
#
#  stack                  remaining: * 4 * 5 -
#    3
# (1 + 2)
#
# Next item in the string = '*'
# '*' is an operator, so pop two items of the stack, join, then push
#
#  stack                  remaining: 4 * 5 -
# ((1 + 2) * 3)
#
# Next item in the string = '4'
# '4' is a number so push it onto the stack
#
#  stack                  remaining: * 5 -
#    4
# ((1 + 2) * 3)
#
# Next item in the string = '*'
# '*' is an operator, so pop two items of the stack, join, then push
#
#  stack                  remaining: 5 -
# ((1 + 2) * 3) * 4)
#
# Next item in the string = '5'
# '5' is a number so push it onto the stack
#
#  stack                  remaining: -
#    5
# ((1 + 2) * 3) * 4)
#
# Next item in the string = '-'
# '-' is an operator, so pop two items of the stack, join, then push

#  stack                  remaining:
# (((1 + 2) * 3) * 4) - 5)
#
# Now we just pop the last item off the stack, and we have our answer.

Here is the powershell which makes this all possible.

function PostfixTo-Infix
{
    param
    (
        [Parameter(Mandatory=$true)][string]$Postfix
    )
    process
    {
        # Split the string into the individual peices
        $values = $Postfix.Split(' ', [StringSplitOptions]::RemoveEmptyEntries)

        # Stack to store the values as they are being parsed
        $stack = [System.Collections.Generic.Stack[string]]::new()

        foreach($val in $values)
        {
            # Try to parse the value
            $intvalue = 0
            if([int]::TryParse($val, [ref]$intvalue))
            {
                # If the value is an integer, add it to the stack
                $stack.Push($val)

                # Then continue on
                continue;
            }
            else
            {
                # The value is an operator, so pop off the previous two values,
                # And join them with the operator. 
                $b = $stack.Pop();
                $a = $stack.Pop();

                # Then push the result onto the stack
                $stack.Push("($a $val $b)")
            }
        }

        # The final item on the stack must be our result.
        return $stack.Pop()
    }
}

There you have it. Results look like this:

PostfixTo-Infix '1 2 - 3 4 * 5 + *'
((1 - 2) * ((3 * 4) + 5))

In the spirit of good programming, here is a code golfy powershell one liner which also works. It mostly uses the same principal, but uses a hashtable as the stack. One caveat is that it prints all results out backwards, which is fine because there are parenthesis everywhere.

function PostfixTo-InfixGolf
{
    param
    (
        [Parameter(Mandatory=$true)][string]$p
    )
    process
    {
        $s=@{};$a=0;switch -regex($p.Split(' ')){'\d'{$s[$a++]=$_}default{$t="($($s[--$a]) $_ $($s[--$a]))";$s[$a++]=$t}};$s[--$a]
    }
}

bookmark_borderPowershell Postfix Evaluator

There are three different notations for expressing a mathematical equation.

  • prefix
  • infix
  • postfix

Basically, the just describe where operators lie in the equation compared to their operands. The most common of all of the formats is the one everyone is probably most familiar with, infix, which means that the operators lie in between their operands. Lets take for example the equation “1 + 5”.

#
# Equation: "1 + 5"
#
#  Prefix:  "+ 1 5"
#  Infix:   "1 + 5"
#  Postfix: "1 5 +"
#

From the perspective of a computer, postfix if very easy to process, because an operator always follows its two operands, and their is no reason to use parenthesis. Prefix notation also does not require parenthesis, but is a little strange to work with.

In order to process prefix, we always take the right most operator in the expression, and apply it to the next two digits in the string. We do that until we are out of operators. Infix is processed using the order of operations. Postfix is processed by taking the left most operand in the expression, and applying it to the preceding digits every time.

#
# Equation1: "(1 + 5) * 3"
# Equation2: "1 + 5 * 3"
#
#  Prefix1:  "* + 1 5 3"   => "* 6 3"   => "18"
#  Infix1:   "(1 + 5) * 3" => "(6) * 3" => "18"
#  Postfix1: "3 1 5 + *"   => "3 6 *"   => "18"
#
#
#  Prefix2:  "+ * 3 5 1"   => "+ 15 1"  => "16"
#  Infix2:   "1 + 5 * 3"   => "1 + 15"  => "16"
#  Postfix2: "1 5 3 * +"   => "1 15 +"  => "16"
#

The postfix algorithm is quite simple to implement. Going through the string, every time you see a number, add it to a stack. If you see an operator, pop the last two items off of the stack and apply the operator. Push the result back onto the stack so that it can be used in following computations.

So here is an example of the postfix algorithm implemented in powershell.

function Eval-Postfix
{
    param
    (
        [Parameter(Mandatory = $true)][string]$expression
    )
    process
    {
        # Create a stack to store the numbers which aren't yet processed
        $stack = [System.Collections.Generic.Stack[int]]::new()

        # Split the input string into individual values
        $values = $expression.Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)

        foreach($val in $values)
        {
            # Try to parse the value
            $intvalue = 0
            if([int]::TryParse($val, [ref]$intvalue))
            {
                # If the value is an integer, add it to the stack
                $stack.Push($intvalue)

                # Then continue on
                continue;
            }

            # If the value is not an integer, we pop the top two numbers off the stack
            $b = $stack.Pop()
            $a = $stack.Pop()

            # Then perform the correct operation on them
            $result = switch($val)
            {
                '+' { $a + $b; break; }
                '-' { $a - $b; break; }
                '*' { $a * $b; break; }
                '/' { $a / $b; break; }
            }

            # Push the result back onto the stack
            $stack.Push($result)
        }

        # The only thing left on the stack should be a single value,
        # which is the result of the final operation
        return $stack.Pop()
    }
}

Just to be safe, here is code golf one liner which uses the same queue approach to solve the problem.

function Eval-Postfix
{
    param
    (
        [Parameter(Mandatory = $true)][string]$e
    )
    process
    {
        $s=@();switch -regex($e.Split(' ')){'\d'{$s=,+$_+$s}default{$a,$b,$s=$s;$s+=iex "$b$_$a"}};$s
    }
}

The code golf version uses a powershell array rather than a stack, and uses a switch as a loop. There you have it, both of these functions will output the results of postfix operations.

#
#  > Eval-Postfix "1 2 + 5 6 * +"
#     33
#

bookmark_borderPascals Triangle In SQL

Looking around online, I could not find a great example of how to draw Pascal’s Triangle in SQL. The solution is quite cool in SQL, because management studio prints out a nice grid for you. Here are three examples. The first one is simple and well documented, the second one is a little more squished, and the third one is a complete dumpster fire that I got too by looking up how to code golf in SQL.



CREATE PROCEDURE PascalsTriangle
    @Levels INT
AS
BEGIN
    SET NOCOUNT ON

    -- Declare variables
    DECLARE @counter        INT = 2
    DECLARE @innerCount     INT
    DECLARE @tmp            DECIMAL(38,0)
    DECLARE @tmp2           DECIMAL(38,0)
    DECLARE @dquery         NVARCHAR(2048)

    -- Create the results table with its one column
    CREATE TABLE #result
    (
        [1] DECIMAL(38,0)
    )
    INSERT INTO #result VALUES (1)

    -- For every level beyond 1
    WHILE(@counter <= @Levels)
    BEGIN
        
        -- Add another column to to the table
        EXEC('ALTER TABLE #result ADD [' + @counter + '] DECIMAL(38,0)')

        -- Select the previous row into a temp table
        SELECT *
        INTO #row
        FROM #result
        ORDER BY 1
        OFFSET (@counter - 2) ROWS
        FETCH NEXT 1 ROWS ONLY

        -- Re-initialize the temp variables, and loop counter
        SET @tmp = 1
        SET @tmp2 = 1
        SET @innerCount = 2

        -- For every column beyond 1
        WHILE(@innerCount <= @counter)
        BEGIN

            -- Create a query which selects the current value of the cell to back it up.
            SET @dquery = 'SELECT @t = [' + CAST(@innerCount AS NVARCHAR(10)) + '] FROM #row'
            EXEC sp_executesql @dquery, N'@t DECIMAL(38,0) OUTPUT', @t = @tmp2 OUTPUT

            -- Set the value of this cell in this row to that + @tmp
            EXEC('UPDATE #row SET [' + @innerCount + '] = ISNULL([' + @innerCount + '], 0) + ' + @tmp)

            -- Reset the temp variable
            SET @tmp = @tmp2

            -- Increment the count
            SET @innerCount += 1

        END

        -- Insert the row into result
        INSERT INTO #result
        SELECT * FROM #row

        -- Increment the counter
        SET @counter += 1

        -- Drop the row temp table
        DROP TABLE #row
    END

    -- Output the result
    SELECT * FROM #result
    DROP TABLE #result
END


There is another post in PowerShell which does a much better job actually explaining details about how this triangle works. But long story short, every item is the sum of the two above it added together. So now let’s make the script a little smaller.



CREATE PROCEDURE PascalsTriangle2
	@Levels INT
AS
BEGIN
	SET NOCOUNT ON
	DECLARE @o INT=2
	DECLARE @ INT
	DECLARE @1 DECIMAL(38,0)
	DECLARE @2 DECIMAL(38,0)
	DECLARE @q NVARCHAR(2048)
	CREATE TABLE #o([1] DECIMAL(38,0))
	INSERT #o VALUES(1)
	o:
 		EXEC('ALTER TABLE #o ADD['+@o+']DECIMAL(38,0)')
 		SELECT *INTO #r FROM #o ORDER BY 1 OFFSET(@o-2)ROWS	FETCH NEXT 1 ROWS ONLY
 		SET @1 = 1
 		SET @2 = 1
 		SET @ = 2
 		i:
			SET @q = CONCAT('DECLARE @ TABLE (val DECIMAL(38,0));UPDATE #r SET [',@,']=ISNULL([',@,'], 0)+',@1,'OUTPUT deleted.[',@,']INTO @;SET @t=(SELECT *FROM @)')
			EXEC sp_executesql @q,N'@t DECIMAL(38,0) OUTPUT',@t=@2 OUTPUT  			
  			SET @1 = @2
  			SET @ += 1
 		IF @ <= @o GOTO i
 		INSERT #o
 		SELECT * FROM #r
 		SET @o += 1
 		DROP TABLE #r
	IF @o <= @Levels GOTO o
	SELECT * FROM #o
	DROP TABLE #o
END

As you can see, I removed all the pesky comments which were just getting in the way of looking small. Also, the while loops were all turned into GOTO loops to take up a little less space. The variable names were shortened a little as well.

DECLARE @s NVARCHAR(MAX)='CREATE PROCEDURE PascalsTriangle3
@Levels INT
AS
BEGIN
	SET NOCOUNT ON
	~o |=2~ |~1 &amp;~2 &amp;~q NVARCHAR(2048)CREATE TABLE #o([1] &amp;)INSERT #o VALUES(1)
	o:EXEC(''ALTER TABLE #o ADD[''+@o+'']&amp;'')$|O #r ^#o ORDER BY 1 OFFSET(@o-2)ROWS	FETCH NEXT 1 ROWS ONLY!1 = 1!2 = 1! = 2
 		i:!q = CONCAT(''~ TABLE (val &amp;);UPDATE #r SET ['',@,'']=ISNULL(['',@,''], 0)+'',@1,''OUTPUT deleted.['',@,'']|O @;!t=($^@)'')EXEC sp_executesql @q,N''@t &amp; OUTPUT'',@t=@2 OUTPUT!1 = @2!+=1IF @ <= @o GOTO i
 		INSERT #o$ ^#r!o += 1%#r IF @o <= @Levels goto o$ ^#o%#o
END'
SELECT @s=REPLACE(@s,LEFT(i,1),SUBSTRING(i,2,20))
FROM(VALUES('~ DECLARE @'),('! SET @'),('&amp; DECIMAL(38,0)'),('% DROP TABLE '),('$ SELECT *'),('^ FROM '),('| INT'))a(i)
EXEC(@s)

Now there’s the real beast. It does a bunch of replacements on a string, to make some longer valid SQL, then executes the SQL dynamically to produce the stored procedure. That’s some production quality code.

bookmark_borderPascals Triangle In Powershell

A programming question that I’m sure everyone has been asked at some point during the interview is ‘Pascal’s Triangle’. Besides solving it, nothing would impress your interviewer more than solving it in PowerShell, with support for massively huge numbers. Wikipedia could probably explain this better than I can, but the premise is simple: start with ‘1’, then each row is one element longer than the previous and is made up of the to values above it added together (Except for the values on either end, which are always 1).

function Pascals-Triangle
{
    [CmdLetBinding()]
    param
    (
        # Single parameter is the number of levels
        [Parameter(Mandatory=$true)][ValidateRange(1, [int]::MaxValue)][int]$Levels
    )
    process
    {
        # Create a dummy previous array, just so that we know what is going on.
        $prev = [System.Numerics.BigInteger[]]::new(0)

        # Now for each level
        for($l = 0; $l -lt $Levels; $l++)
        {
            # Create the current working array
            $current = [System.Numerics.BigInteger[]]::new($prev.Length + 1)
            
            # We know for sure that the first and the last element are 1 (This avoids bounds checking in the loop below)
            $current[0] = 1
            $current[$current.Length - 1] = 1

            # Now for all other elements add the previous two together
            for($i = 1; $i -lt $current.Length - 1; $i++)
            {
                $current[$i] = $prev[$i - 1] + $prev[$i]
            }

            # Write out our result
            Write-Output ([string]::Join(' ', $current))

            # Set the previous to the current for the next iteration
            $prev = $current
        }

    }
}

This is most likely the solution that any interviewer would be expecting, because it is by far the simplest to follow. The idea is simple, start with an array of zero length and for every level of the triangle allocate an array which is 1 element longer, and populate it by adding together elements from the previous array. I’m not sure why, but whenever I got this question in college I wanted to do it recursively, which totally screwed me. Also, the reason behind using ‘BigInteger’, is that these number expand very rapidly. Try it out with ‘int’ or even ‘ulong’, and you wont be able to do too many levels. Anyway, the results in powershell end up looking like this.

1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1

So now, to mix things up, lets do it in a way that doesn’t require us to keep reallocating arrays. This next solution does the whole thing in place, reusing the same array each time. This is not a big deal, because we always know how long the array needs to be when we start. (Length = Levels). We can just go through and add up the values of the previous row each time.

function Pascals-Triangle2
{
    [CmdLetBinding()]
    param
    (
        # Single parameter is the number of levels
        [Parameter(Mandatory=$true)][ValidateRange(1, [int]::MaxValue)][int]$Levels
    )
    process
    {
        # Create our working array
        $row = [System.Numerics.BigInteger[]]::new($Levels)

        # Now for each level
        for($l = 1; $l -le $Levels; $l++)
        {
            # The first index is always 1, and set our previous
            $iPrevious = $row[0] = 1
            for($i = 1; $i -lt $l; $i++)
            {
                # Back up the current value
                $temp = $row[$i]

                # Add in the previous value
                $row[$i] += $iPrevious

                # Swap out the previous
                $iPrevious = $temp
            }

            # Write out our result
            Write-Output ([string]::Join(' ', ($row | select -First $l)))
        }

    }
}
        

And there we have it. So what about a more obfuscated version for those really hard core interviewees? Lets do it. Taking solution 2 and squishing everything together as much as possible we get a horribly confusing solution.

function Pascals-Triangle3
{
    [CmdLetBinding()]
    param
    (
        # Single parameter is the number of levels
        [Parameter(Mandatory=$true)][ValidateRange(1, [int]::MaxValue)][int]$Levels
    )
    process
    {
        ($r=[System.Numerics.BigInteger[]]::new(($l=$Levels)+1))[0]=1
        1..$l|%{"$($r|?{$_-ne0})";$t=1;1..$_|%{$a=$r[$_];$r[$_]+=$t;$t=$a}}
    }
}

I’m sure everyone is better at code golf than I am, but this solution already confuses me so it should be a good start.

bookmark_borderPowershell IPv4 to IPv6 and Expand IPv6

Here is the easy answer using built in .NET functions:

#
# Converts an IPV4 address to an IPV6 address.
#
function Map-IPv4ToIPv6
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true, 
                   Position = 0,
                   ValueFromPipeline=$true,
                   ValueFromPipelineByPropertyName=$true)][String]$IPv4
    )
    process
    {
        [ipaddress]$parsedAddress = $null;
        if (-not [ipaddress]::TryParse($IPv4, [ref]$parsedAddress))
        {
            throw "Could not parse IPV4 address $IPv4"
        }

        # Already done
        if ($parsedAddress.AddressFamily -eq [System.Net.Sockets.AddressFamily]::InterNetworkV6)
        {
            return $parsedAddress;
        }
        elseif ($parsedAddress.AddressFamily -eq [System.Net.Sockets.AddressFamily]::InterNetwork)
        {
            return $parsedAddress.MapToIPv6();
        }
    }
}

#
# Converts an IPV6 address to an IPV4 address.
#
function Map-IPv6ToIPv4
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true, 
                   Position = 0,
                   ValueFromPipeline=$true,
                   ValueFromPipelineByPropertyName=$true)][String]$IPv6
    )
    process
    {
        [ipaddress]$parsedAddress = $null;
        if (-not [ipaddress]::TryParse($IPv6, [ref]$parsedAddress))
        {
            throw "Could not parse IPv6 address $IPv6"
        }

        # Already done
        if ($parsedAddress.AddressFamily -eq [System.Net.Sockets.AddressFamily]::InterNetwork)
        {
            return $parsedAddress;
        }
        elseif ($parsedAddress.AddressFamily -eq [System.Net.Sockets.AddressFamily]::InterNetworkV6)
        {
            return $parsedAddress.MapToIPv4();
        }
    }
}

For a more detailed, and “manual” approach, you can keep reading.

IPv4 is simple, 32 bits of easy to understand goodness. Every single IPv4 address is made up of 4 bytes. For example “192.168.1.1”. There is no shorthand either, that is the address, that’s it. There is no better way to write it. Ok, so now that that is out of the picture, what is IPv6.

IPv6 is 128 bits of a little bit more confusing. Well actually, it’s not the address that is confusing, it’s the notation. When you see IPv6, a lot of times you see things like “::1” and “FE80::1”. What does that even mean. How is that 128 bits. How will we ever know. So the secret is in the “::”, which is pretty much just shorthand for “a lot of zeros go here”.

For example: if we take the address “::1”, it actually expands out to “0000:0000:0000:0000:0000:0000:0000:0001”. Literally just means fill in this space with zeros. Another example: “FE80::1” becomes “FE80:0000:0000:0000:0000:0000:0000:0001”. Just FYI, you are only allowed to use the “::” once, otherwise addresses would be unreadable. (“::1::”) So before anything, here is a function to automate that, because it drives me nuts.

#
# Expand an IPv6 address. For example ::1 becomes 0000:0000:0000:0000:0000:0000:0000:0001
#
function Expand-IPV6
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true, 
                   Position = 0,
                   ValueFromPipeline=$true,
                   ValueFromPipelineByPropertyName=$true)][String]$IPv6
    )
    process
    {
        $count = 0
        $loc = -1

        # Count the number of colons, and keep track of the double colon
        for($i = 0; $i -lt $IPv6.Length; $i++) 
        { 
            if($IPv6[$i] -eq ':') 
            {
                $count++
                if(($i - 1) -ge 0 -and $IPv6[$i-1] -eq ':')
                {
                    $loc = $i
                }
            }
        }

        # If we didnt find a double colon and the count isn't 7, then throw an exception
        if($loc -lt 0 -and $count -ne 7)
        {
            throw "Invalid IPv6 Address"
        }

        # Add in any missing colons if we had a double
        $cleaned = $IPv6
        if($count -lt 7)
        {
            $cleaned = $IPv6.Substring(0, $loc) + (':'*(7-$count)) + $IPv6.Substring($loc)
        }

        # Parse current values in fill in new IP with hex numbers padded to 4 digits
        $result = @()
        foreach($splt in $cleaned -split ':')
        {
            $val = 0
            $r = [int]::TryParse($splt, [System.Globalization.NumberStyles]::HexNumber, [System.Globalization.CultureInfo]::InvariantCulture, [ref]$val)
            $result += ('{0:X4}' -f $val)
        }

        return $result -join ':'
    }
}

So this does the expansions work for us, because it’s a pain in the butt to deal with these addresses in their short form. Also, I know the code is gross and I apologize for that, but there is more to come. Ok, so now, what happens if we have an IPv4 address and we need to turn it into IPv6. Boom, its easy. The last 32 bits of the IPv6 address are equal to our IPv4 address, and the 16 bits preceding those are all set to 1. Easy enough! That means, the IP address 1.1.1.1 would turn into “::FFFF:0101:0101” or “0000:0000:0000:0000:0000:FFFF:0101:0101” in full form. So how do we make that happen?

# Map an IPv4 address to IPv6
function Map-IPv4ToIPv6
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true, 
            Position = 0,
            ValueFromPipeline=$true,
            ValueFromPipelineByPropertyName=$true)][String]$IPv4
    )
    process
    {
        # Split on the dots
        $split = $IPv4 -split '\.'
        if($split.Length -ne 4)
        {
            throw 'Not a valid IPv4 Address'
        }

        # Parse into numbers
        $vals = @()
        foreach($v in $split)
        {
            $vals += [int]::Parse($v)
        }

        # Return as shorthand ipv6
        return "::FFFF:{0:X2}{1:X2}:{2:X2}{3:X2}" -f $vals
    }
}

BOOM, mission accomplished. Hopefully (it worked in a few tests). Also, for completeness, here is a function which turns an IPv4 address which is represented in IPv6 back into an IPv4 address.

#
# Transforms an IPv4 address which is represented in IPv6 back into IPv4
#
function Map-IPv6ToIPv4
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true, 
            Position = 0,
            ValueFromPipeline=$true,
            ValueFromPipelineByPropertyName=$true)][String]$IPv6
    )
    process
    {
        # Get the expanded form
        $expanded = Expand-IPV6 $IPv6
        $split = $expanded.Split(':')

        # Make sure this is valid...
        $is4MappedTo6 = $split[5] -match 'FFFF'
        if(-not $is4MappedTo6)
        {
            throw 'This is not an IPv4 Address mapped to IPv6'
        }

        # Parse each byte as an integer
        $addr = @()
        for($i = 0; $i -lt 8; $i+=2)
        {
            $addr += [int]::Parse(($split[6 + [math]::Floor(($i / 4))][($i % 4)..(($i%4)+1)] -join ''), [System.Globalization.NumberStyles]::HexNumber)
        }

        return $addr -join '.'
    }
}

The classes built in to handle IP addresses are definitely much smarter, and the way to go, but hopefully this has explained an easy way to do it.

bookmark_borderPowershell Color Conversions

Alright. I was in a bit of a groove last night. I was up till 3, but I do have something amazing to show for it. Like literally fantastic. I have no idea how humanity got to this point without some of this code. So without further ado how about some scripts for doing color conversions in powershell. They aren’t commented amazingly. Well the first one is ok. But the other ones suck. Anyway, here it is. These are based entirely off of the wikipedia article on HSV and HSL. I did my best to use those formulas exaclty so everything *SHOULD* work.



#
# All of these functions are based on the formulas provided on WikiPedia for color conversions
# So check them out for more info https://en.wikipedia.org/wiki/HSL_and_HSV
#

#
# Converts from Hue Saturation Lightness to RGB
#
function Convert-HSLToRGB
{
    [CmdLetBinding()]
    param
    (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipelineByPropertyName = $true)][ValidateRange(0, 360)][double]$Hue,
        [Parameter(Mandatory = $true, Position = 1, ValueFromPipelineByPropertyName = $true)][ValidateRange(0, 1)][double]$Saturation,
        [Parameter(Mandatory = $true, Position = 2, ValueFromPipelineByPropertyName = $true)][ValidateRange(0, 1)][double]$Lightness
    )
    process
    {
        # You will need to check wikipedia for this. Essentially
        # We take a cube, and project it on to a 2d surface.
        # Which gives us a hexagon. Seriously, look at wikipedia. 
        # It weird to think about, but imaging a rubix cube, sitting on one of its corners.
        # If you look directly down at it there will be one corner in the middle, and 6 along the side
        # The chroma is the distance from the center of the hexagon
        # The Hue is our angle along this hexagon.
        # WIKIPEDIA LOOK AT IT.

        # Calculate the chroma
        $chroma = (1 - [Math]::Abs(2.0*$Lightness - 1.0)) * $Saturation
        
        # Figure out which section of the hexagon we are in. (6 sections)
        $H = $Hue / 60.0
        $X = $chroma * (1.0 - [Math]::Abs($H % 2 - 1))

        # Get value for lightness
        $m = $Lightness - 0.5 * $chroma
        $rgb = @($m, $m, $m)

        # UGH this part.
        # ok. So this is what happens. [R, G, B] is definited peicewise.
        # 0 <= H < 1 then [C, X, 0]
        # 1 <= H < 2 then [X, C, 0]
        # 2 <= H < 3 then [0, C, X]
        # 3 <= H < 4 then [0, X, C]
        # 4 <= H < 5 then [X, 0, C]
        # 5 <= H < 6 then [C, 0, X]
        # So these calculate the index of the x value and the c value
        $xIndex = (7 - [Math]::Floor($H)) % 3
        $cIndex = [int]($H / 2) % 3

        # Add the values in
        $rgb[$xIndex] += $X
        $rgb[$cIndex] += $chroma

        # Return the value
        return [pscustomobject]@{red = [int]($rgb[0] * 255); green = [int]($rgb[1] * 255); blue = [int]($rgb[2] * 255)}
    }
}

#
# Converts from Hue Saturation Value to RGB
#
function Convert-HSVToRGB
{
    [CmdLetBinding()]
    param
    (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipelineByPropertyName = $true)][ValidateRange(0, 360)][double]$Hue,
        [Parameter(Mandatory = $true, Position = 1, ValueFromPipelineByPropertyName = $true)][ValidateRange(0, 1)][double]$Saturation,
        [Parameter(Mandatory = $true, Position = 2, ValueFromPipelineByPropertyName = $true)][ValidateRange(0, 1)][double]$Value
    )
    process
    {
        # This one works very similar to the funciton above, so take a look there for comments
        $chroma = $Value * $Saturation
        $H = $Hue / 60.0
        $X = $chroma * (1.0 - [Math]::Abs($H % 2 - 1))

        $m = $Value - $chroma
        $rgb = @($m, $m, $m)

        $xIndex = (7 - [Math]::Floor($H)) % 3
        $cIndex = [int]($H / 2) % 3

        $rgb[$xIndex] += $X
        $rgb[$cIndex] += $chroma

        return [pscustomobject]@{red = [int]($rgb[0] * 255); green = [int]($rgb[1] * 255); blue = [int]($rgb[2] * 255)}
    }
}

#
# Converts from RGB to Hue Saturation Value
#
function Convert-RGBToHSV
{
    [CmdLetBinding()]
    param
    (
        [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)][ValidateRange(0, 0xFF)][int]$Red,
        [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)][ValidateRange(0, 0xFF)][int]$Green,
        [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)][ValidateRange(0, 0xFF)][int]$Blue
    )
    process
    {
        $arry = @(($red / 255), ($green / 255), ($blue / 255))
        $max = 0
        $min = 0
        for($i = 1; $i -lt $arry.Length; $i++)
        {
            if($arry[$i] -gt $arry[$max])
            {
                $max = $i
            }

            if($arry[$i] -lt $arry[$min])
            {
                $min = $i
            }
        }
        $C = $arry[$max] - $arry[$min]
        $hue = 0
        $saturation = 0
        if(0 -ne $C)
        {
            $hue = ($arry[($max + 4) % 3] - $arry[($max+5) % 3]) / $C

            if(0 -eq $max)
            {
                $hue %= 6
            }
            else
            {
                $hue += 2*$max
            }

            $hue *= 60
            $value = $arry[$max]
            $saturation = $C / $value
        }
        return [pscustomobject]@{hue = $hue; saturation = $saturation; value = $value}
    }
}

#
# Converts from RGB to Hue Saturation Lightness
#
function Convert-RGBToHSL
{
    [CmdLetBinding()]
    param
    (
        [Parameter(Mandatory=$true, Position=0, ValueFromPipelineByPropertyName=$true)][ValidateRange(0, 0xFF)][int]$Red,
        [Parameter(Mandatory=$true, Position=1, ValueFromPipelineByPropertyName=$true)][ValidateRange(0, 0xFF)][int]$Green,
        [Parameter(Mandatory=$true, Position=2, ValueFromPipelineByPropertyName=$true)][ValidateRange(0, 0xFF)][int]$Blue
    )
    process
    {
        $arry = @(($red / 255), ($green / 255), ($blue / 255))
        $max = 0
        $min = 0
        for($i = 1; $i -lt $arry.Length; $i++)
        {
            if($arry[$i] -gt $arry[$max])
            {
                $max = $i
            }

            if($arry[$i] -lt $arry[$min])
            {
                $min = $i
            }
        }
        $C = $arry[$max] - $arry[$min]
        $hue = 0
        $lightness = 0
        if(0 -ne $C)
        {
            $hue = ($arry[($max + 4) % 3] - $arry[($max+5) % 3]) / $C

            if(0 -eq $max)
            {
                $hue %= 6
            }
            else
            {
                $hue += 2*$max
            }

            $hue *= 60
            $lightness = 0.5 * ($arry[$min] + $arry[$max])
            $saturation = $C / (1 - [Math]::Abs(2.0 * $lightness - 1))
        }
        return [pscustomobject]@{hue = $hue; saturation = $saturation; lightness = $lightness}
    }
}

#
# Splits an integer RGB value into 3 parts
#
function Split-RGB
{
    param
    (
        [Parameter(Mandatory=$true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)][ValidateRange(0, 0xFFFFFF)][int]$RGB
    )
    process
    {
        return [pscustomobject]@{red = ($RGB -shr 16); green = (($RGB -shr 8) -band 0xFF); blue = ($RGB -band 0xFF)}
    }
}

#
# Combines the rgb components into a single value
#
function Join-RGB
{
    param
    (
        [Parameter(Mandatory=$true, Position=0, ValueFromPipelineByPropertyName=$true)][ValidateRange(0, 0xFF)][int]$Red,
        [Parameter(Mandatory=$true, Position=1, ValueFromPipelineByPropertyName=$true)][ValidateRange(0, 0xFF)][int]$Green,
        [Parameter(Mandatory=$true, Position=2, ValueFromPipelineByPropertyName=$true)][ValidateRange(0, 0xFF)][int]$Blue
    )
    process
    {
        return ($Red -shl 16) -bor ($Green -shl 8) -bor $Blue
    }
}

#
# Just gets an rgb color object
#
function Get-RGBColor
{
    param
    (
        [Parameter(Mandatory=$true, Position=0, ValueFromPipelineByPropertyName=$true)][ValidateRange(0, 0xFF)][int]$Red,
        [Parameter(Mandatory=$true, Position=1, ValueFromPipelineByPropertyName=$true)][ValidateRange(0, 0xFF)][int]$Green,
        [Parameter(Mandatory=$true, Position=2, ValueFromPipelineByPropertyName=$true)][ValidateRange(0, 0xFF)][int]$Blue
    )
    process
    {
        return [pscustomobject]@{red = $Red; green = $Green; blue = $Blue}
    }
}

# Test
# Join-RGB -Red 45 -Green 32 -Blue 222 | Split-RGB | Join-RGB | Split-RGB
# Get-RGBColor 10 20 30 | Convert-RGBToHSL | Convert-HSLToRGB
# Get-RGBColor 10 20 30 | Convert-RGBToHSV | Convert-HSVToRGB
# Get-RGBColor 10 20 30 | Convert-RGBToHSV | Convert-HSVToRGB | Convert-RGBToHSL | Convert-HSLToRGB


So yea. There is it. I hope someone can make use of these things. I don’t really have I use for them, just a need to fight something throught until it is done. So my issue is that I was working on a side project, and we were using an external web service, which always returned colors in HSL. To be honest, I had no idea what that was, so I did some reading and had to write a function to convert to RGB. RGB makes SO much more sense… to me.

bookmark_borderPowershell Text Flip

Here’s a fun piece of code. All it does is take a string, and turn it over.

function Flip-Text
{
    [CmdLetBinding()]
    param
    (
        [Parameter(Mandatory=$true, ValueFromPipeline=$true, Position=0)][String]$text
    )
    process
    {
        $rightSide='abcdefghijklmnopqrstuvwxyz0123456789&amp;_<>?!()[]{}'
        $upsidedown='ɐqɔpǝɟƃɥıɾʞʃɯuodbɹsʇnʌʍxʎz012Ɛᔭ59Ɫ86⅋‾><¿¡)(][}{'
        $output = [System.Text.StringBuilder]::New($text.Length);
        for($i = $text.Length -1; $i -ge 0; $i--)
        {
            # Get the character
            $c = $text[$i]

            # Find the index of the character in the right side up array.
            $indR = $rightSide.IndexOf([char]::ToLower($c))

            if($indR -gt 0)
            {
                $replacement = $upsidedown[$indR];
                if ([char]::IsUpper($c))
                {
                    # Try to make it upper case
                    $replacement = [char]::ToUpper($replacement);
                }

                # Stringbuilder returns a value when you append. 
                # Assigning to null to prevent.
                $null = $output.Append($replacement)
            }
            else{
                $null = $output.Append($c)
            }
        }

        return $output.ToString()
    }
}

So there was the program. All it really does, it maps right side up characters to their inverse. In the spirit of the rest of this site, here is an ugly one liner which does the same thing. Pretty much it’s just the first answer all mushed together, but it sure looks cool.

function Flip-Text2
{
    [CmdLetBinding()]
    param
    (
        [Parameter(Mandatory=$true, ValueFromPipeline=$true, Position=0)][String]$text
    )
    process
    {
        ($text[($text.Length-1)..0]|%{'ɐqɔpǝɟƃɥıɾʞʃɯuodbɹsʇnʌʍxʎz012Ɛᔭ59Ɫ86⅋‾><¿¡)(][}{ '[("$(97..122|%{[char]$_})$(0..9)&amp;_<>?!()[]{}"-replace' ','').IndexOf($_)]})-join''
    }
}

This one doesn’t support any upper case characters.