Categories
Command Line

Improving ‘diff’ readability on Windows | Tip

The output of diff -q path1 path2 is pretty verbose. This function

  • Converts full paths to relative
  • Differences are red.

Missing ‘diff’ ?

If git is installed, you may need to update your %PATH% environment variable.

# in your profile
$Env:Path = "$Env:ProgramFiles\Git\usr\bin", $Env:Path -join ';'

Stand-Alone function Compare-Directory

This is an isolated version of Ninmonkey.Console: Compare-Directory . I removed all dependencies except coloring is provided by the module PoshCode/Pansies.

function Invoke-NativeCommand {
    <#
    .synopsis
        wrapper to both call 'Get-NativeCommand' and invoke an argument list
    .example
        PS> # Use the first 'python' in path:
        Invoke-NativeCommand 'python' -Args '--version'
    #>

    param(
        # command name: 'python' 'ping.exe', extension is optional
        [Parameter(Mandatory, Position = 0)]
        [string]$CommandName,

        # Force error if multiple  binaries are found
        [Parameter()][switch]$OneOrNone,

        # native command argument list
        [Alias('Args')]
        [Parameter(Position = 1)]
        [string[]]$ArgumentList
    )

    $binCommand = Get-NativeCommand $CommandName -OneOrNone:$OneOrNone -ea Stop
    & $binCommand @ArgumentList
}

function Compare-Directory {
    <#
    .SYNOPSIS
    Compare Two directories using 'diff'
    .EXAMPLE
    Compare-Directory 'c:\foo' 'c:\bar\bat'
    #>
    [Alias('DiffDir')]
    param(
        # Path1
        [Parameter(Mandatory, Position = 0)]
        [string]$Path1,

        # Path2
        [Parameter(Mandatory, Position = 1)]
        [string]$Path2,

        # Output original raw text?
        [Parameter()][switch]$OutputRaw
    )

    $Base1 = $Path1 | Get-Item -ea Stop
    $Base2 = $Path2 | Get-Item -ea Stop
    $Label1 = $Base1 | Split-Path -Leaf | New-Text -fg 'green'
    $Label2 = $Base2 | Split-Path -Leaf | New-Text -fg 'yellow'

    "Comparing:
        Path: $Path1
        Path: $Path2
    " | Write-Information

    $stdout = Invoke-NativeCommand 'diff' -args @(
        '-q'
        $Base1
        $Base2
    )

    $outColor = $stdout
    $outColor = $outColor -replace [regex]::Escape($path1), $Label1
    $outColor = $outColor -replace [regex]::Escape($path2), $Label2
    $outColor = $outColor -replace 'Only in', (New-Text 'Only In' -fg 'red')
    $outColor = $outColor -replace 'Differ', (New-Text 'Differ' -fg 'red')

    if ($OutputRaw) {
        h1 'Raw' | Write-Information
        $stdout
        return
    }

    $outColor
}

function Get-NativeCommand {
    <#
    .synopsis
        wrapper that returns Get-Item on a native command
    .example
        # if you want an error when multiple options are found
        PS> Get-NativeCommand python -OneOrNone
    .example
        # note: this is important, $cmdArgs to be an array not scalar for '@' usage
        $binPy = Get-NativeCommand python
        $cmdArgs = @('--version')
        & $binPy @cmdArgs
    .example
    #>
    [cmdletbinding()]
    param(
        # Name of Native .exe Application
        [Parameter(Mandatory, Position = 0, ValueFromPipeline)]
        [object]$CommandName,

        # One or None: Raise errors when there are more than one match
        [Parameter()][switch]$OneOrNone
    )

    process {
        try {
            $query = Get-Command -Name $CommandName -All -CommandType Application -ea Stop
            | Sort-Object Name

        } catch [CommandNotFoundException] {
            Write-Error "ZeroResults: '$CommandName'"
            return
        }

        if ($OneOrNone -and $query.Count -gt 1) {
            $query | Format-Table -Wrap -AutoSize -Property Name, Version, Source
            Write-Error "OneOrNone: Multiple results for '$CommandName'"
            return
        }

        if ($query.Count -gt 1) {
            $query = $query | Select-Object -First 1
        }

        Write-Debug "Using Item: $($query.Source)"
        $query
    }
}
Categories
Command Line PowerShell Quick Tips References And Cheat Sheets

Easy way to cache results on the Command Line | Power Shell Tip

Sometimes you’ll need to run a command with the same input with different logic.
This can be a hassle using a slow command like Get-ADUser or Get-ChildItem on a lot of files like ~ (Home) with -Depth / -Recurse

ls ~ -Depth 4 | Format-Table Name

PowerShell 7.0+

Powershell 7 added the Ternary Operator, and several operators for handling $null values.

All of these examples will only run Get-ChildItem the first time. Any future calls are cached.

Null-Coalesce ??= Assignment Operator

This is my favorite on the Command line. The RHS (Right Hand Side) skips evaluation if the left side is not $null

$AllFiles ??= ls ~ -Depth 4

Using the Null-Coalesce ?? Operator

$AllFiles = $AllFiles ?? ( ls ~ -Depth 4  )

Ternary Operator ? whenTrue : WhenFalse

$allFiles = $allFiles ? $allFiles : ( ls ~ -Depth 4 )

Windows PowerShell and Powershell < 7

Windows Powershell can achieve the same effect with an if statement

if(! $AllFiles) { $AllFiles = ls ~ -Depth 4 }
Categories
Power BI Power Query Quick Tips

Preserving Types When using “Add Custom Column” in Power Query

The default UI sets your column to type any.

You can use the optional argument of Table.AddColumn to set it to number
Or you can declare your function’s return type

Why doesn’t the original [Num] * 2 work?

Powerquery does not know what type will be returned by your function. That’s because each is by definition a function that returns type any

See More

Categories
PowerShell Quick Tips What's New

PowerShell : Prefixing lines with the Pipe operator |

There’s a lot of ways to use line continuations in Windows Powershell without backticks . Powershell added a new one, the | pipe operator. It’s cleaner to read, and makes it easier to insert, delete, or toggle comments on the console.

Now you can write:

# Powershell
ls | sort Length
| Select -First 10
| ft Name, Length

Instead of piping on line endings

# Windows Powershell
ls | sort Length |
Select -First 10 |
ft Name, Length

# or
ls | sort Length | Select -First 10 | ft Name, Length
Categories
Formatting PowerShell Snippets

Reusable Calculated Properties in PowerShell

The function Label is from the module: github.com/Ninmonkey.Console

$basePath = 'C:\Program Files'
$calcProp = @{}
$calcProp.FileSize = @{
    n         = 'Size'
    e         = { $_.Length | Format-FileSize }
    Alignment = 'right'
}
# relative path defaults to relative your current location, so I override it
$calcProp.RelativePath = @{
    n = 'RelativePath'
    e = {
        Push-Location $baseBath
        $_.FullName | Resolve-Path -Relative
        Pop-Location
    }
}
$calcProp.ColorizedName = @{
    n = 'ColorName'
    e = {
        $Color = ($_ | Test-IsDirectory) ? 'blue' : 'green'
        Label $_.Name -fg $Color -Separator ''
    }
}

$DefaultPropList = 'Name', $calcProp.ColorizedName, $calcProp.RelativePath

Label 'basePath' $basePath

$files = Get-ChildItem $basePath -Depth 2
$files | Format-Table Name, Length, $calcProp.FileSize, $calcProp.RelativePath

$files | Format-Table  $calcProp.FileSize, $calcProp.ColorizedName

# Using pre-declared property lists, which may include scriptblocks like $calcProp.FileSize
$files | Format-Table -Property $DefaultPropList

Categories
Power BI Quick Tips

Tip: Controlling a Column’s “Summarize By” Type in Power BI

The Model view allows you to set a global default “Summarize By” type for every column.
You still have the ability to override the aggregation type per-visual in Report view