NathanMUtilities/NathanMUtilities.psm1

278 lines
18 KiB
PowerShell
Raw Normal View History

2020-03-24 19:41:00 +00:00
<EFBFBD><EFBFBD># This module is a collection of things I find useful while working
# interactively with PowerShell. Because contents may be updated significantly,
# or moved to more appropriate locations as they mature, its contents should
# *only* be used interactively and not in scripts.
#
# These may also have additional dependencies such as non-PowerShell utilities
# and filesystem structure.
#
# TODO: Add function to automatically format a file with line numbers and then compare the input and output with diffmerge
function PrintXml([xml]$xml) {
$xml.Save([Console]::Out)
}
<#
.SYNOPSIS
Return an ISO 8601 timestamp (UTC) string in long format
#>
function Get-8601Long {
(Get-Date).ToUniVersalTime().ToString("s") + "Z"
}
<#
.SYNOPSIS
Return an ISO 8601 timestamp (UTC) string in short format
#>
function Get-8601 {
(Get-8601Long).Replace("-", "").Replace(":", "")
}
<#
A text search function meant to be comparable to grep
The main advantage is that the encoding of input files will automatically be inferred, and matching
will be performed on the resulting unicode, instead of naively just doing ascii matching
TODO: Might be better to do incremental matching (match by lines instead of doing gc -raw)
#>
function Select-StringGrep {
Param(
[Parameter(Mandatory = $True, Position = 0)]
$Expression,
[Parameter(ValueFromRemainingArguments = $True)]
[string[]]
$File,
[Parameter(ValueFromPipeline = $True)]
$InputObject,
[switch]
$Recursive,
[switch]
$Matches,
[switch]
[alias("N")]
$LineNumbers,
[switch]
$OnlyMatching,
[switch]
$Count,
$MaxCount,
[switch]
$CaseSensitive
)
Begin {
$rgx = [regex]$Expression
}
Process {
ls $File -File | % {
$filename = (Resolve-Path -Relative $_.FullName)
$content = (gc $filename -raw)
if ($content) {
$rgx.Matches((gc $filename -raw)) |
% {
if ($Matches) {
$_
}
else {
$filename + ":" + $_.Value
}
}
}
}
}
}
New-Alias psgrep Select-StringGrep
function New-TempDir() {
$time = (Get-Date).ToUniversalTime().ToString("yyyyMMddTHHmmss.ffffZ")
$tempDir = mkdir "~/temp/$time"
$tempDir.FullName
}
function Out-FileNoBom {
Param(
[Parameter(ValueFromPipeline = $True)]
$InputObject,
[Parameter(Position = 0)]
$Path
)
# Output file as UTF-8 with no BOM; see http://stackoverflow.com/questions/5596982/using-powershell-to-write-a-file-in-utf-8-without-the-bom
Begin {
New-Item -ItemType File -Path $Path -Force
$streamWriter = New-Object System.IO.StreamWriter $Path
$streamWriter.AutoFlush = $True
}
Process {
$streamWriter.WriteLine($InputObject)
}
End {
$streamWriter.Close()
}
}
<#
.DESCRIPTION
Given a Fanuc macro file, normalize it by removing some extraneous whitespace and renaming it to it's program name.
This allows comparison between files that have been loaded on a machine and files that have not.
TODO: attempt to remove parentheses inside parentheses (regex can probably only handle single nesting)
#>
function NormalizeFanucMacroFile {
Param($InputFile, $OutputDirectory)
$re = ([regex]"^([^()]+)(\(.*\))$")
Write-Verbose "Normalizing $InputFile"
$fileContents = gc $InputFile -raw
$programName = ([regex]"O\d+").Matches($fileContents)[0].Value
$fileContents = $fileContents -replace ' ', ''
$fileContents = $fileContents -replace '`t', ''
$fileContents = $fileContents -replace '((\r)?\n){3,}', "`r`n`r`n"
$fileContents = $fileContents -replace '(\r?\n)+$', ''
Out-FileNoBom -InputObject $fileContents (Join-Path $OutputDirectory $programName) | Out-Null
unix2dos (Join-Path $OutputDirectory $programName) | Out-Null
(Join-Path $OutputDirectory $programName)
}
function NormalizeAndCompareFanucMacros($InputFile1, $InputFile2) {
$tempDir1 = New-TempDir
$tempDir2 = New-TempDir
$file1 = NormalizeFanucMacroFile -InputFile1 $InputFile1 -OutputDirectory $tempDir1
$file2 = NormalizeFanucMacroFile -InputFile2 $InputFile2 -OutputDirectory $tempDir2
sgdm $file1 $file2 /t1=$InputFile1 /t2=$InputFile2
}
# TODO: Display informative names in the comparison
function NormalizeAndCompareFanucMacroDirs($Directory1, $Directory2) {
$Output1 = New-TempDir
$Output2 = New-TempDir
ls -file $Directory1 | % {NormalizeFanucMacroFile -InputFile $_.FullName -OutputDirectory $Output1} | Out-Null
ls -file $Directory2 | % {NormalizeFanucMacroFile -InputFile $_.FullName -OutputDirectory $Output2} | Out-Null
sgdm $Output1 $Output2 /t1=$Directory1 /t2=$Directory2
}
function New-EIModuleManifest($Path) {
New-ModuleManifest -CompanyName Electroimpact -Copyright "(c) $((Get-Date).Year) Electroimpact inc. All Rights reserved." -Path $Path
}
$NewlineRegex = [regex]"\r?\n"
# Create a newline index that is used with regex results and the
# MatchIndexToLine to get the line number of a match.
function CreateNewlineIndexList($Text) {
$NewlineRegex.Matches($Text).Index
}
# Get the line number of an index (character offset in text)
function GetLineNumberOfIndex([int]$MatchIndex, $NewlineIndexList) {
[Math]::Abs([Array]::BinarySearch($NewlineIndexList, $MatchIndex))
}
Function FormatPSFile($Path) {
PSScriptAnalyzer\Invoke-Formatter (Get-Content $Path -Raw) > $Path
}
function Get-PSFileFunctions($file) {
[System.Management.Automation.Language.Parser]::ParseFile($file, [ref]$null, [ref]$null).endblock.statements.name
}
function NewNotepadPlusPlusNote() {
new-item ~/temp/$(get-8601) | % {npp -multiInst -notabbar $_.fullname}
}
<#
Generates hashes for a set of files and stores them in a csv file.
They can later be validated against the generated file using ValidateHashList
#>
function GenerateHashList {
Param(
[Parameter(ValueFromRemainingArguments = $True, Position = 0)]
$Paths,
$OutputFile = "hashes.csv",
$Algorithm = "SHA256"
)
$hashes = @()
foreach ($Path in $Paths) {
$hashes += Get-FileHash $Path -Algorithm $Algorithm
}
$hashes | % { $_.Path = (Resolve-Path $_.Path -Relative); $_} | ConvertTo-Csv -NoTypeInformation > $OutputFile
}
<#
A function to test hash lists generated like:
Get-FileHash * | %{ $_.Path = (Resolve-Path $_.Path -Relative); $_} | ConvertTo-Csv -NoTypeInformation > hashes.csv
ValidateHashList hashes.csv
Returns boolean
#>
function ValidateHashList {
[CmdLetBinding()]
Param(
$HashFile
)
$hashes = gc $HashFile | ConvertFrom-Csv
$allMatch = $True
foreach ($hashEntry in $hashes) {
if ((Get-FileHash $hashEntry.Path -Algorithm $hashEntry.Algorithm).Hash -ne $hashEntry.Hash) {
Write-Error "Hashes do not match for $($hashEntry.Path)"
$allMatch = $False
}
}
$allMatch
}
# Attempt to format files as XML before Vault Comparison
# Use like: Compare-VaultItems '.\Sub2 Resynch.xml' -ScriptBlock $FormatXmlTemp
$FormatXmlTemp = {
$ItemTempFile = [System.IO.Path]::GetRandomFileName()
$tempPath = (Join-Path $Env:TEMP $ItemTempFile)
([xml](gc $args[0])).Save($tempPath)
$tempPath
}
<#
.SYNOPSIS
For the given module(s), look for an examples directory in its installed location, then run all the examples
#>
function Invoke-ModuleExamples {
[CmdletBinding()]
Param(
$Module,
$Repository = "EIPowerShellRepo"
)
if ($Module) {
$installedModules = Get-InstalledModule $Module
}
else {
$installedModules = Get-InstalledModule
}
$modules = $installedModules | ? {$_.Repository -eq $Repository}
foreach ($module in $modules) {
echo "Executing examples for $($module.Name)"
$examplesDir = (Join-Path $module.InstalledLocation examples)
if (-not (Test-Path $examplesDir)) {
Write-Error "No examples directory for $($module.Name)"
continue
}
Push-Location $examplesDir
$exampleScripts = Get-ChildItem *.ps1
foreach ($scriptFile in $exampleScripts) {
echo "Executing $(Resolve-Path $scriptFile)"
gc $scriptFile | ? {$_ -and $_ -ne ""} | % {Write-Host "`r`n$(Prompt)$_"; invoke-expression $_}
}
Pop-Location
}
}