# 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) | fzf --multi --bind ctrl-a:toggle-all,ctrl-d:page-down,ctrl-u:page-up,ctrl-t:toggle } else { $global:pse = Search-Everything -Global @($args) | fzf --multi --bind ctrl-a:toggle-all,ctrl-d:page-down,ctrl-u:page-up,ctrl-t:toggle } $pse }