320 lines
20 KiB
PowerShell
320 lines
20 KiB
PowerShell
# 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
|
||
# TODO: not suck
|
||
|
||
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't handle arbitraty nesting depth)
|
||
#>
|
||
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
|
||
}
|
||
}
|
||
|
||
Import-Module PSEverything
|
||
# I care about the path
|
||
[PSEverything.Everything]::SetMatchPath($true)
|
||
|
||
<#
|
||
Do a quick 'Everything' search and store the results in $pse.
|
||
Also print out the results.
|
||
|
||
TODO: Why is 'less' text display sometimes black?
|
||
#>
|
||
$global:pse = [string[]]@()
|
||
function pse{
|
||
Param(
|
||
[Alias('l')]
|
||
[switch]$Local
|
||
[switch]$NoPage
|
||
)
|
||
|
||
|
||
[PSEverything.Everything]::SetMatchPath($true)
|
||
|
||
if($Local){
|
||
$global:pse = Search-Everything $args
|
||
} else {
|
||
$global:pse = Search-Everything -Global $args
|
||
}
|
||
|
||
if($pse.Count -eq 0){
|
||
return
|
||
}
|
||
|
||
# Show results with their index in $pse
|
||
$display = 0..($pse.Count - 1) | %{ "$_. $(($global:pse)[$_])" }
|
||
if($pse.Count -le 10 -or $NoPage) {
|
||
$display
|
||
} else {
|
||
# Paginate output, start view at the top
|
||
$display | less +0
|
||
}
|
||
}
|