Common.ps1
# References: # 1. Below are the list of predefined vars that can be used: # - $PSScriptRoot [System defined] The folder path for current scipt file, NOT the caller script to call this function using namespace System.Management.Automation # MyProfile module env vars $MyProfileInstalledListVarName = "MyProfileInstalledList" $MyProfileBinPathVarName = "MyProfileBinPath" $MyProfilePSModulesPathVarName = "MyProfilePSModulePath" $MyProfileModuleDevPathVarName = "MyProfileModuleDevPath" $MyProfileModuleLastUpdateFileTimeUtcVarName = "MyProfileModuleLastUpdateFileTimeUtc" # MyProfile module paths $MyProfileModuleMyProfileShortcutPath = Join-Path $PSScriptRoot "MyProfile.lnk" $MyProfileModuleTemplateRootPath = Join-Path $PSScriptRoot "Template" # MyProfile template relative vars $MyProfileRelativeManifestPath = "MyProfileManifest.psd1" $MyProfileRelativeHelpPath = "MyProfileHelp.ps1" $MyProfileRelativeBinPath = "bin" $MyProfileRelativeSetupPath = "Setup" $MyProfileRelativePSMoudlesPath = "PowerShell\Modules" $MyProfileRelativeCmdletsPath = "PowerShell\Cmdlets" $MyProfileRelativePSProfilePath = "PowerShell\PSProfile.ps1" $MyProfileRelativeSysProfilePath = "System\SysProfile.ps1" ############################################################################### # Helper functions ############################################################################### enum ColorHostWriteLevel { Verbose = 0 Debug = 1 Information = 2 Highlight = 3 Warning = 4 Keynote = 5 Error = 6 } function ConvertTo-ColorHostWriteLevel { param( [Parameter(Position=0)] [string] $Level ) [ColorHostWriteLevel]$result = [ColorHostWriteLevel]::Information if ([ColorHostWriteLevel]::TryParse($Level, $true, [ref] $result)) { return $result } else { return [ColorHostWriteLevel]::Information } } <# .SYNOPSIS Get the minimum write level for Write-ColorHost. #> function Get-ColorHostMinWriteLevel { return "$(ConvertTo-ColorHostWriteLevel $Global:ColorHostMinWriteLevel)" } <# .SYNOPSIS Set the minimum write level for Write-ColorHost. .DESCRIPTION Set the minimum level to write for Write-ColorHost. All levels below this will not be writen. .PARAMETER Level The level of message to write the object as. It is used to mapping with message purpose, write level and color. - Verbose Color=<Default> Level=0 For verbose information which is similar to the concept in Write-Verbose. - Debug Color=<Default> Level=1 For Debug information which is similar to the concept in Write-Debug. - Information Color=<Default> Level=2 The default level which is similar to the concept in Write-Host with default color. - Highlight Color=Cyan Level=3 For the information to highlight. The importancy is similar to Warning, but it is more for good/important things. - Warning Color=Yellow Level=4 For Warning information which is similar to the concept in Write-Warning. - Keynote Color=Magenta Level=5 For information which is considered as keynote. The importancy is similar to Error, but it is more for good/important things. - Error Color=Red Level=6 For Warning information which is similar to the concept in Write-Error. #> function Set-ColorHostMinWriteLevel { param( [Parameter(Position=0)] [ValidateSet('Verbose', 'Debug', 'Information', 'Highlight', 'Warning', 'Keynote', 'Error')] [string] $Level = 'Information' ) $oldLevel = Get-ColorHostMinWriteLevel if ($oldLevel -eq $Level) { Write-Host "The minimum write level for Write-ColorHost is the same, no need to change." } else { $Global:ColorHostMinWriteLevel = $Level Write-Host "The minimum write level for Write-ColorHost is updated from $oldLevel to $Global:ColorHostMinWriteLevel." } } <# .SYNOPSIS Write host with embedded color tag support and multi level message support. .DESCRIPTION A new way to write host with color by adding color tag inside output string. 1. All colors in Write-Host can also be used as tag here. E.g.: Black, DarkBlue, DarkGreen, DarkCyan, DarkRed, DarkMagenta, DarkYellow, Gray, DarkGray, Blue, Green, Cyan, Red, Magenta, Yellow, White 2. Color tag is case insensitive. 3. Use Level to support multi level message printing, and to map to different color. By default, the minimum write level is "Information". .PARAMETER Object The Object to write. For string, embedded color tags are supported. .PARAMETER NoNewLine Specifies that the content displayed in the console does not end with a newline character. .PARAMETER Level The level of message to write the object as. It is used to mapping with message purpose, write level and color. - Verbose Color=<Default> Level=0 For verbose information which is similar to the concept in Write-Verbose. - Debug Color=<Default> Level=1 For Debug information which is similar to the concept in Write-Debug. - Information Color=<Default> Level=2 The default level which is similar to the concept in Write-Host with default color. - Highlight Color=Cyan Level=3 For the information to highlight. The importancy is similar to Warning, but it is more for good/important things. - Warning Color=Yellow Level=4 For Warning information which is similar to the concept in Write-Warning. - Keynote Color=Magenta Level=5 For information which is considered as keynote. The importancy is similar to Error, but it is more for good/important things. - Error Color=Red Level=6 For Warning information which is similar to the concept in Write-Error. .EXAMPLE Write-ColorHost "This is an <RED>single</RED> tag example!" Write-ColorHost "This is an <Red>multi</Red> <Green>tags</Green> example!" -Level Highlight Write-ColorHost "This is <Blue>an <Red>nested</Red> tags example</Blue>!" -Level Error #> function Write-ColorHost { param ( [Parameter(Position=0)] [object] $Object, [switch] $NoNewLine, [ValidateSet('Verbose', 'Debug', 'Information', 'Highlight', 'Warning', 'Keynote', 'Error')] [string] $Level = 'Information' ) # Verify the write level $curWriteLevel = ConvertTo-ColorHostWriteLevel $Level $minWriteLevel = ConvertTo-ColorHostWriteLevel $ColorHostMinWriteLevel if ($curWriteLevel -lt $minWriteLevel) { return } # Start to really write content to host. $colorTagRegex = '(<\/?(?:Black|DarkBlue|DarkGreen|DarkCyan|DarkRed|DarkMagenta|DarkYellow|Gray|DarkGray|Blue|Green|Cyan|Red|Magenta|Yellow|White)>)' $colorStack = New-Object 'system.collections.generic.stack[string]' $curColor = switch ($Level) { 'Highlight' { 'Cyan' } # Reason: Blue looks not good, Green is often used to show something success. So Cyan is the best one. 'Warning' { 'Yellow' } # Reason: Match the color of Write-Warning 'Keynote' { 'Magenta' } # Reason: This color is most similar to the color for Error. 'Error' { 'Red' } # Reason: Match the color of Write-Error default { '' } } if ($Object -is [string]) { $message = [string]$Object [regex]::Split($Message, $colorTagRegex, 1) | % { if ([regex]::IsMatch($_, $colorTagRegex, 1)) { $color = $_ -replace '[<>\/]' if ($_ -like '</*>') { $curColor = if ($colorStack.Count -gt 0) { $colorStack.Pop() } else { '' } } else { $colorStack.Push($curColor) $curColor = $color } } else { if ([string]::IsNullOrWhiteSpace($curColor)) { Write-Host $_ -NoNewline } else { Write-Host $_ -NoNewline -ForegroundColor $curColor } } } if (-not $NoNewLine) { Write-Host '' } } else { if ([string]::IsNullOrWhiteSpace($curColor)) { Write-Host $Object -NoNewline:$NoNewLine } else { Write-Host $Object -NoNewline:$NoNewLine -ForegroundColor $curColor } } } function Convert-EnvironmentVariableTarget { param( [Parameter(Position=0, Mandatory = $true)] [ValidateSet('Machine', 'User', 'Process')] [string] $Target ) $targetMapping = @{ Machine = [EnvironmentVariableTarget]::Machine User = [EnvironmentVariableTarget]::User Process = [EnvironmentVariableTarget]::Process } return $targetMapping[$Target] } function Set-EnvironmentVariable { [CmdletBinding()] param( [Parameter(Position=0, Mandatory = $true)] [string] $Name, [Parameter(Position=1, Mandatory = $true)] [AllowNull()] [AllowEmptyString()] [string] $Value, [Parameter(Position=2, Mandatory = $true)] [ValidateSet('Machine', 'User', 'Process')] [string] $Target ) $targetType = Convert-EnvironmentVariableTarget $Target # Always expand the value when targeting to "Process" if ($Target -eq 'Process') { $Value = [Environment]::ExpandEnvironmentVariables($Value) } $oldValue = [Environment]::GetEnvironmentVariable($Name, $targetType) if ($oldValue -ne $value){ [Environment]::SetEnvironmentVariable($Name, $Value, $targetType) if ($Target -ne 'Process' -AND ([string]::IsNullOrEmpty($Value) -OR $Value.Contains('%'))) { # Use another way to save with "REG_EXPAND_SZ" type as [Environment]::GetEnvironmentVariable only saved it as "REG_SZ" $regPath = switch ($Target) { 'User' { "Registry::HKEY_CURRENT_USER\Environment" } "Machine" { "Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" } } Set-ItemProperty -Path $regPath -Name $Name -Value $Value -Type ExpandString } } if ($Target -ne 'Process') { [Environment]::SetEnvironmentVariable($Name, [Environment]::ExpandEnvironmentVariables($Value), [EnvironmentVariableTarget]::Process) } } function Get-EnvironmentVariable { [CmdletBinding()] param( [Parameter(Position=0, Mandatory = $true)] [string] $Name, [Parameter(Position=1)] [ValidateSet('Machine', 'User', 'Process')] [string] $Target = 'Process', [switch] $NotExpand ) $targetType = Convert-EnvironmentVariableTarget $Target if ($NotExpand -AND $Target -ne 'Process') { $registrySubKey = switch ($Target) { "User" { [Microsoft.Win32.Registry]::CurrentUser.OpenSubKey("Environment", [Microsoft.Win32.RegistryKeyPermissionCheck]::ReadSubTree) } "Machine" { [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey("SYSTEM\CurrentControlSet\Control\Session Manager\Environment", [Microsoft.Win32.RegistryKeyPermissionCheck]::ReadSubTree) } } return $registrySubKey.GetValue($Name, $null, [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames) } return [Environment]::GetEnvironmentVariable($Name, $targetType) } function Set-EnvironmentListVariable { [CmdletBinding()] param( [Parameter(Position=0, Mandatory = $true)] [string] $Name, [Parameter(Position=1, Mandatory = $true)] [AllowNull()] [AllowEmptyCollection()] [string[]] $ListValue, [Parameter(Position=2, Mandatory = $true)] [ValidateSet('Machine', 'User', 'Process')] [string] $Target, [switch] $AllowDuplicate ) if (-NOT $AllowDuplicate) { $ListValue = $ListValue | Select-Object -Unique } $value = ($ListValue | ? { $_ -ne $null }) -join ';' Set-EnvironmentVariable -Name $Name -Value $value -Target $Target } function Get-EnvironmentListVariable { [CmdletBinding()] param( [Parameter(Position=0, Mandatory = $true)] [string] $Name, [Parameter(Position=1)] [ValidateSet('Machine', 'User', 'Process')] [string] $Target = 'Process', [switch] $NotExpand ) return @((Get-EnvironmentVariable -Name $Name -Target $Target -NotExpand:$NotExpand) -split ';' | % { $_.Trim() } | ? { -NOT [string]::IsNullOrWhiteSpace($_) }) } function Add-EnvironmentListVariableValues { [CmdletBinding()] param( [Parameter(Position=0, Mandatory = $true)] [string] $Name, [Parameter(Position=1, Mandatory = $true)] [string[]] $ValuesToAppend, [Parameter(Position=2, Mandatory = $true)] [ValidateSet('Machine', 'User', 'Process')] [string] $Target, [switch] $AppendFront, [switch] $AllowDuplicate ) $curValueList = Get-EnvironmentListVariable -Name $Name -Target $Target -NotExpand if (-NOT $AllowDuplicate) { $curValueList = $curValueList | Select-Object -Unique } $newValueList = if ($AppendFront) { @($ValuesToAppend) + $curValueList } else { @($curValueList) + $ValuesToAppend } if (-NOT $AllowDuplicate) { $newValueList = $newValueList | Select-Object -Unique } if ($newValueList.Count -gt $curValueList.Count) { Set-EnvironmentListVariable -Name $Name -ListValue $newValueList -Target $Target } } function Remove-EnvironmentListVariableValues { [CmdletBinding()] param( [Parameter(Position=0, Mandatory = $true)] [string] $Name, [Parameter(Position=1, Mandatory = $true)] [string[]] $ValuesToRemove, [Parameter(Position=2, Mandatory = $true)] [ValidateSet('Machine', 'User', 'Process')] [string] $Target ) $curValueList = Get-EnvironmentListVariable -Name $Name -Target $Target $newValueList = $curValueList | ? { -NOT($ValuesToRemove -contains $_) } if ($curValueList.Count -gt $newValueList.Count) { Set-EnvironmentListVariable -Name $Name -ListValue $newValueList -Target $Target } } function InjectCodeBlock { param( [string] $Target, [string] $CodeId, [string] $CodeBlock ) if (($Target -is [string]) -AND ($Target.Contains("<$CodeId>"))) { $Target = ReplaceCodeBlock -Target $Target -CodeId $CodeId -CodeBlock $CodeBlock } else { $Target = $Target.TrimEnd() $Target += "`r`n`r`n# <$CodeId>`r`n" $Target += "$CodeBlock" $Target += "`r`n# </$CodeId>`r`n" } return $Target.Trim() } function ReplaceCodeBlock { param( [string] $Target, [string] $CodeId, [string] $CodeBlock ) $powerShellProfile -replace "(?ms)^(`r`n)*# <$CodeId>.*# </$CodeId>(`r`n)*","`r`n`# <$CodeId>`r`n$CodeBlock`r`n# </$CodeId>`r`n`r`n" } function RemoveCodeBlock { param( [string] $Target, [string] $CodeId ) $powerShellProfile -replace "(?ms)^(`r`n)*# <$CodeId>.*# </$CodeId>(`r`n)*","`r`n" } function InstallPsProfile { param( [string] $CodeId, [Parameter(Mandatory=$true, ParameterSetName="ScriptString")] [string] $ScriptString, [Parameter(Mandatory=$true, ParameterSetName="ScriptBlock")] [ScriptBlock] $ScriptBlock ) $powerShellProfileDir = Join-Path $([Environment]::GetFolderPath("MyDocuments")) "WindowsPowerShell" $powerShellProfilePath = Join-Path $PowerShellProfileDir "Microsoft.PowerShell_profile.ps1" # Create a empty profile if it does not exists if (-not (Test-Path $powerShellProfilePath)) { New-Item -Path $powerShellProfilePath -ItemType File -Force } if ($PsCmdlet.ParameterSetName -eq "ScriptBlock") { $ScriptString = $ScriptBlock.ToString() } $ScriptString = $ScriptString.Trim() $powerShellProfile = Get-Content $powerShellProfilePath -Raw InjectCodeBlock -Target $powerShellProfile -CodeId $CodeId -CodeBlock $ScriptString | Set-Content $powerShellProfilePath } |