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 <a href="https://github.com/ninmonkey/Ninmonkey.Console">Ninmonkey.Console: Compare-Directory</a> . 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
}
}
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
Power BI Discord asked the question: > How do you use duplicate keys, and dynamic urls with <a href="https://docs.microsoft.com/en-us/powerquery-m/web-contents">Web.Contents</a> ? (They were using a web API that required duplicate keys) After I wrote this, Chris Webb found an easier solution.
Requirements
You can’t use a regular record because keys must be distinct. Query = [ Key1 = 1, Key1 = 10] will throw an error.
You can’t put the **dynamic url** in the first argument Web.Contents or else refreshes can break
let
QueryStr_UsingDuplicateKeys = (key as text, values as list) as text =>
// values are the 'value' of 'key'-> 'value' pairs
let
escapedList = List.Transform(
values,
each
key & "=" & Uri.EscapeDataString( Text.From(_) )
),
joinedArgs = Text.Combine(escapedList, "&")
in
joinedArgs
in
QueryStr_UsingDuplicateKeys
Note: BaseUrl is for the static part of the url. Everything else should be in options[RelativePath] or options[Query] See docs: Web.Contents for details.
Which results in the url: <a href="http://jsonplaceholder.typicode.com/comments">http://jsonplaceholder.typicode.com/comments</a> This is a good place to use Power Query‘s null coalesce operator (Which isn’t in the official docs)
The default UI sets your column to type any.You can use the optional argument of Table.AddColumn to set it to numberOr 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
The only function that Power BI is missing is Facebook.Graph (on my machine)
* This query checks for missing identifiers (which may be functions). ( You can filter by type if you convert #shared to a table instead of calling Record.FieldNames() )
Generating the list using #shared
To get a list of all identifiers (functions, variables, constants) I use the variable named #shared . Create a new blank query, then paste this
let
IdentifierList = List.Sort(Record.FieldNames( #shared ))
in
IdentifierList
I copy using Copy Entire List, then storing the results into a PowerShell variable. I Repeat the same with Power BI.
That’s more than I expected.
To find out exactly which functions are different, use the Power Shell operator -NotIn
# Find functions in PBI but not Excel
$MissingExcel = $PowerBI | ? { $_ -notin $Excel }
# Find functions in Excel but not PBI
$MissingPowerBI = $Excel | ? { $_ -notin $PowerBI }
The list of functions Power BI is Missing
Facebook.Graph
The list of functions Excel is Missing
Note: This is the list for today, on my machine. Run the #shared query to find any changes.
ForEach-Object ( Note: this is a command, not a control loop )
It’s first argument is a ScriptBlock so it appears like a control loop.
Notice return verses break in ForEach-Object . Remember that the { ... stuff ... } in this case is an anonymous function , not a language control statement It is a parameter to the ForEach-Object . When not specified, it’s the -Process Parameter