PowerProfile.Core.psm1
#Requires -Version 5.1 #region Module Script Variables if ($null -eq $PSStyle) { # Lazy support for PSStyle for PS version <7.2.0 $Script:PSStyle = [PSCustomObject]@{ Reset = "`e[0m" BlinkOff = "`e[25m" Blink = "`e[5m" BoldOff = "`e[22m" Bold = "`e[1m" HiddenOff = "`e[28m" Hidden = "`e[8m" ReverseOff = "`e[27m" Reverse = "`e[7m" ItalicOff = "`e[23m" Italic = "`e[3m" UnderlineOff = "`e[24m" Underline = "`e[4m" StrikethroughOff = "`e[29m" Strikethrough = "`e[9m" Foreground = @{ Black = "`e[30m" Red = "`e[31m" Green = "`e[32m" Yellow = "`e[33m" Blue = "`e[34m" Magenta = "`e[35m" Cyan = "`e[36m" White = "`e[37m" BrightBlack = "`e[90m" BrightRed = "`e[91m" BrightGreen = "`e[92m" BrightYellow = "`e[93m" BrightBlue = "`e[94m" BrightMagenta = "`e[95m" BrightCyan = "`e[96m" BrightWhite = "`e[97m" } Background = @{ Black = "`e[40m" Red = "`e[41m" Green = "`e[42m" Yellow = "`e[43m" Blue = "`e[44m" Magenta = "`e[45m" Cyan = "`e[46m" White = "`e[47m" BrightBlack = "`e[100m" BrightRed = "`e[101m" BrightGreen = "`e[102m" BrightYellow = "`e[103m" BrightBlue = "`e[104m" BrightMagenta = "`e[105m" BrightCyan = "`e[106m" BrightWhite = "`e[107m" } } $Parameters = @{ MemberType = 'ScriptMethod' InputObject = $PSStyle Name = 'FormatHyperlink' Value = { return $("`e]8;;"+$args[1]+"`e\"+$args[0]+"`e]8;;`e\") } } Add-Member @Parameters } #endregion #region Functions: Config function Set-PoProfileConfig { <# .SYNOPSIS Add/change/remove a PowerProfile config item .DESCRIPTION Adds an item to the PowerProfile configuration .PARAMETER Name Key name of the state item .PARAMETER Value Value of the state item .PARAMETER Remove Removes the desired state item .LINK https://PowerProfile.sh/ #> [CmdletBinding(DefaultParameterSetName='SetKey')] Param( [Parameter(Mandatory=$True,Position=0,ParameterSetName='SetKey')] [Parameter(Mandatory=$True,Position=0,ParameterSetName='RemoveKey')] [string]$Name, [Parameter(Mandatory=$True,Position=1,ParameterSetName='SetKey')] [AllowEmptyString()] [AllowNull()] $Value, [Parameter(Mandatory=$True,ParameterSetName='RemoveKey')] [switch]$Remove ) if ($Value -eq [bool]::TrueString -or $Value -eq [bool]::FalseString) { $Value = [System.Convert]::ToBoolean($Value) } if ($Remove) { $PoProfileConfig.PSObject.Properties.Remove($Name) } elseif ($null -eq $PoProfileConfig.PSObject.Properties.Item($Name)) { $PoProfileConfig | Add-Member -MemberType NoteProperty -Name $Name -Value $Value } else { $PoProfileConfig.$Name = $Value } $p = [System.IO.Path]::Combine( $env:XDG_CONFIG_HOME, $( if ($IsWindows) { 'PowerShell' } else { 'powershell'} ), 'Modules', 'PowerProfile', 'PowerProfile.state.json' ) if (($PoProfileConfig.PSObject.Properties).Count -gt 0) { $baseDir = Split-Path -Path $p if (-Not ([System.IO.Directory]::Exists($baseDir))) { $null = New-Item -Type Container -Force $baseDir -ErrorAction Stop } ConvertTo-Json $PoProfileConfig -Compress | Set-Content -Path $p -Encoding ASCII } elseif ([System.IO.File]::Exists($p)) { Remove-Item -Path $p -ErrorAction Ignore } } function Get-PoProfileConfig { <# .SYNOPSIS Get PowerProfile config .DESCRIPTION Reads the PowerProfile config .PARAMETER Name Return the value of a specific config item only .OUTPUTS String, when specifying $Name, otherwise PSObject. .LINK https://PowerProfile.sh/ #> [CmdletBinding()] Param( [string]$Name ) $p = [System.IO.Path]::Combine( $env:XDG_STATE_HOME, $( if ($IsWindows) { 'PowerShell' } else { 'powershell' } ), 'Modules', 'PowerProfile', 'PowerProfile.state.json' ) if (-Not (Get-Variable -Scope Script -Name 'PoProfileState' -ErrorAction Ignore)) { if ([System.IO.File]::Exists($p)) { $PoProfileConfig = [System.IO.File]::ReadAllText($p) | ConvertFrom-Json -ErrorAction Ignore } else { $PoProfileConfig = New-Object PSObject } } if ($Name -and $PoProfileConfig.$Name) { $PoProfileConfig.$Name } else { $PoProfileConfig } } function Remove-PoProfileConfig { <# .SYNOPSIS Remove PowerProfile config item .DESCRIPTION Removes a state item from PowerProfile .PARAMETER Name Key name of the state item .LINK https://PowerProfile.sh/ #> [CmdletBinding()] [OutputType([System.Management.Automation.PSObject])] Param( [Parameter(Mandatory=$True)] [string]$Name ) Try { Set-PoProfileConfig -Remove -Name $Name } Catch { Write-Error $_.Exception.Message return } } #endregion #region Functions: State function Set-PoProfileState { <# .SYNOPSIS Add/change/remove a PowerProfile state item .DESCRIPTION Adds an item to the PowerProfile state .PARAMETER Name Key name of the state item .PARAMETER Value Value of the state item .PARAMETER Remove Removes the desired state item .LINK https://PowerProfile.sh/ #> [CmdletBinding()] Param( [Parameter(Mandatory=$True,Position=0,ParameterSetName='SetKey')] [Parameter(Mandatory=$True,Position=0,ParameterSetName='RemoveKey')] [string]$Name, [Parameter(Mandatory=$True,Position=1,ParameterSetName='SetKey')] [AllowEmptyString()] [AllowNull()] $Value, [Parameter(Mandatory=$True,ParameterSetName='RemoveKey')] [switch]$Remove ) if (-Not (Get-Variable -Scope Script -Name 'PoProfileState' -ErrorAction Ignore)) { Get-PoProfileState } if ($Value -eq [bool]::TrueString -or $Value -eq [bool]::FalseString) { $Value = [System.Convert]::ToBoolean($Value) } if ($Remove) { $PoProfileState.PSObject.Properties.Remove($Name) } elseif ($null -eq $PoProfileState.PSObject.Properties.Item($Name)) { $PoProfileState | Add-Member -MemberType NoteProperty -Name $Name -Value $Value } else { $PoProfileState.$Name = $Value } $p = [System.IO.Path]::Combine( $env:XDG_STATE_HOME, $( if ($IsWindows) { 'PowerShell' } else { 'powershell' } ), 'Modules', 'PowerProfile', 'PowerProfile.state.json' ) if (($PoProfileState.PSObject.Properties).Count -gt 0) { $baseDir = Split-Path -Path $p if (-Not ([System.IO.Directory]::Exists($baseDir))) { $null = New-Item -Type Container -Force $baseDir -ErrorAction Stop } ConvertTo-Json $PoProfileState -Compress | Set-Content -Path $p -Encoding ASCII } elseif ([System.IO.File]::Exists($p)) { Remove-Item -Path $p -ErrorAction Ignore } } function Get-PoProfileState { <# .SYNOPSIS Get PowerProfile state .DESCRIPTION Reads the PowerProfile state .PARAMETER Name Return the value of a specific state item only .OUTPUTS String, when specifying $Name, otherwise PSObject. .LINK https://PowerProfile.sh/ #> [CmdletBinding()] Param( [string]$Name ) $p = [System.IO.Path]::Combine( $env:XDG_STATE_HOME, $( if ($IsWindows) { 'PowerShell' } else { 'powershell' } ), 'Modules', 'PowerProfile', 'PowerProfile.state.json' ) if (-Not (Get-Variable -Scope Script -Name 'PoProfileState' -ErrorAction Ignore)) { if ([System.IO.File]::Exists($p)) { $Script:PoProfileState = [System.IO.File]::ReadAllText($p) | ConvertFrom-Json -ErrorAction Ignore } else { $Script:PoProfileState = New-Object PSObject } } if ($Name) { if ($PoProfileState.$Name) { return $PoProfileState.$Name } else { return $null } } else { return $PoProfileState } } function Remove-PoProfileState { <# .SYNOPSIS Remove PowerProfile state item .DESCRIPTION Removes a state item from PowerProfile .PARAMETER Name Key name of the state item .LINK https://PowerProfile.sh/ #> [CmdletBinding()] [OutputType([System.Management.Automation.PSObject])] Param( [Parameter(Mandatory=$True)] [string]$Name ) Try { Set-PoProfileState -Remove -Name $Name } Catch { Write-Error $_.Exception.Message return } } function Reset-PoProfileState { <# .SYNOPSIS Resets PowerProfile state on the local machine .DESCRIPTION Wipes the entire PowerProfile state on the local machine .LINK https://PowerProfile.sh/ #> [CmdletBinding(SupportsShouldProcess,ConfirmImpact='High')] Param( [switch]$Force ) $p = [System.IO.Path]::Combine( $env:XDG_STATE_HOME, $( if ($IsWindows) { 'PowerShell' } else { 'powershell' } ), 'Modules', 'PowerProfile', 'PowerProfile.state.json' ) if ([System.IO.File]::Exists($p)) { if ($Force -or $PSCmdlet.ShouldProcess($p)) { Remove-Item -Path $p -ErrorAction Ignore -Confirm:$false Remove-Variable -Scope Script -Name PoProfileState -ErrorAction Ignore -Confirm:$false New-Variable -Scope Script -Name PoProfileState -Value (New-Object PSObject) } } } #endregion #region Functions: Utilitites function Get-PoProfileSubDirs { [OutputType([array])] Param ( [Parameter(Mandatory=$true)] [string]${Name}, [bool]${Platform}=$true, [bool]${Architecture}=$true, [bool]${Machine}=$true, [ValidateSet('None','Core','Desktop','All')] [string]${PSEditions}='All' ) $PlatformDirectory = '_Platform_' + $(if ($IsMacOS) {'macOS'} elseif ($IsLinux) {'Linux'} else {'Windows'}) $ArchDirectory = '_Arch_' + ($env:PROCESSOR_ARCHITECTURE).ToUpper() $MachineDirectory = '_Machine_' + ($env:COMPUTERNAME).ToUpper() # List profile directories in scope @( if ($Architecture) {$ArchDirectory} if ($Machine) {$MachineDirectory} if ($Platform) { if ($IsCoreCLR -or ($Name -notmatch '^Profile_.*')) { if (-Not $IsWindows) { '_Platform_NonWindows' if ($Architecture) {[System.IO.Path]::Combine('_Platform_NonWindows',$ArchDirectory)} } $PlatformDirectory if ($Architecture) {[System.IO.Path]::Combine($PlatformDirectory,$ArchDirectory)} } } if ($IsCoreCLR) { if (($PSEditions -eq 'All') -or ($PSEditions -eq 'Core')) { '_PSEdition_Core' if ($Architecture) {[System.IO.Path]::Combine('_PSEdition_Core',$ArchDirectory)} if ($Machine) {[System.IO.Path]::Combine('_PSEdition_Core',$MachineDirectory)} } if ($IsWindows) { if (($PSEditions -eq 'All') -or ($PSEditions -eq 'Core')) { ([System.IO.Path]::Combine($PlatformDirectory,'_PSEdition_Core')) if ($Architecture) {[System.IO.Path]::Combine($PlatformDirectory,'_PSEdition_Core',$ArchDirectory)} } if (($PSEditions -eq 'All') -or ($PSEditions -eq 'Desktop')) { ([System.IO.Path]::Combine($PlatformDirectory,'_PSEdition_Desktop')) if ($Architecture) {[System.IO.Path]::Combine($PlatformDirectory,'_PSEdition_Desktop',$ArchDirectory)} if ($Machine) {[System.IO.Path]::Combine($PlatformDirectory,'_PSEdition_Desktop',$MachineDirectory)} } } } ) } function Add-EnvPath { <# .SYNOPSIS Brief synopsis about the function. .DESCRIPTION Detailed explanation of the purpose of this function. #> [CmdletBinding(PositionalBinding=$false)] [OutputType([System.Void])] Param( [string]$Name='PATH', [Parameter(Mandatory=$true,Position=0)] [AllowEmptyString()] [string[]]$Value, [switch]$Prepend ) $p = [System.Environment]::GetEnvironmentVariable($Name) if ($p) { [System.Collections.ArrayList]$p = $p.Split([System.IO.Path]::PathSeparator) } else { [System.Collections.ArrayList]$p = @() } foreach ($v in $Value) { foreach ($i in $v.Split([System.IO.Path]::PathSeparator)) { if ($i -notin $p) { if ($Prepend) { $null = $p.Insert(0,$i) } else { $null = $p.Add($i) } } } } [System.Environment]::SetEnvironmentVariable( $Name, ( [System.Environment]::ExpandEnvironmentVariables($p -Join [System.IO.Path]::PathSeparator)), [System.EnvironmentVariableTarget]::Process ) } function Resolve-RealPath { <# .SYNOPSIS Implementation of Unix realpath(). .DESCRIPTION Implementation of Unix realpath(). .PARAMETER Path Path must exist .LINK https://github.com/PowerProfile/psprofile-common #> [CmdletBinding()] [OutputType([string])] param( [Parameter(Position = 0, Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [Alias('FullName')] [string] $Path ) $Path = $Path | Resolve-Path if (-Not ([System.IO.File]::Exists($Path)) -and -Not ([System.IO.Directory]::Exists($Path))) { return $Path } [string[]] $parts = ($Path.TrimStart([IO.Path]::DirectorySeparatorChar).Split([IO.Path]::DirectorySeparatorChar)) [string] $realPath = '' foreach ($part in $parts) { $changedPath = $false # Change to the path to allow resolving # relative path in link if ([System.IO.Directory]::Exists($realPath)) { Push-Location $realPath $changedPath = $true } $realPath += [string]([IO.Path]::DirectorySeparatorChar + $part) $item = Get-Item $realPath -Force if ($item.Target) { $realPath = $item.Target | Resolve-Path } if ($changedPath) { Pop-Location } } return $realPath } function Get-PoProfileContent { [OutputType([PSCustomObject])] Param() if ($null -eq $Script:PoProfileContent) { $Parameters = @{ Directories = @( $( $p = [System.IO.Path]::Combine($PSScriptRoot,'PSProfile') if ([System.IO.Directory]::Exists($p)) { $p } ) $( foreach ($d in @(Get-Module -ListAvailable -Name ([System.IO.Path]::Combine($PROFILEHOME,'Modules','PowerProfile.*.*')))) { $p = [System.IO.Path]::Combine( (Split-Path $d.Path), 'PSProfile' ) if ([System.IO.Directory]::Exists($p)) { $p } } ) $PROFILEHOME ) Profiles = @(Get-PoProfileProfilesList) } $Script:PoProfileContent = Find-PoProfileContent @Parameters } $PoProfileContent } function Get-PoProfileProfilesList { [OutputType([array])] Param() if ($null -eq $Script:PoProfileProfilesList) { $Script:PoProfileProfilesList = @( 'Profile' 'Profile_' + (($env:LC_PSHOST.ToLower() -replace ' ','') -replace '_','') if ($null -ne $env:TERM_PROGRAM -and $env:TERM_PROGRAM -ne $env:PSHOST_PROGRAM) { 'Profile_' + (($env:LC_TERMINAL.ToLower() -replace ' ','') -replace '_','') } ) } $PoProfileProfilesList } function Find-PoProfileContent { [OutputType([PSCustomObject])] param( [string[]]$Directories=$PROFILEHOME, [string[]]$Profiles='Profile' ) [PSCustomObject]$return = @{ Profiles = @{} Config = @{} PoProfileConfig = @{} Functions = [ordered]@{} Modules = @() Scripts = @() } $keys = @('Config','Functions','Modules','Scripts') # PSProfile directory or PowerProfile bundle directories foreach ($Directory in $Directories) { if ($Directory -ne $PROFILEHOME) { $p = [System.IO.Path]::Combine($Directory,'Modules') if ( [System.IO.Directory]::Exists($p) -and @([System.IO.Directory]::EnumerateDirectories($p,'*','TopDirectoryOnly')).Count -gt 0 ) { $return.Modules += $p } $p = [System.IO.Path]::Combine($Directory,'Scripts') if ( [System.IO.Directory]::Exists($p) -and @([System.IO.Directory]::EnumerateFiles($p,'*.ps1','TopDirectoryOnly')).Count -gt 0 ) { $return.Scripts += $p } } # User Profiles foreach ($PoPr in $Profiles) { $SubDirs = @('') + @(Get-PoProfileSubDirs -Name (Split-Path -Leaf $PoPr) -PSEditions $(if ($IsCoreCLR) {'Core'} else {'Desktop'})) foreach ($SubDir in $SubDirs) { $p = [System.IO.Path]::Combine($Directory,$PoPr,$SubDir) if ([System.IO.Directory]::Exists($p)) { [string[]]$files = [System.IO.Directory]::EnumerateFiles($p,'*.ps1','TopDirectoryOnly') [array]::Sort($files) foreach ($file in $files) { if ($file -match '\.Test\.ps1$') { continue } if( $null -eq $return.Profiles.$PoPr ) { $return.Profiles.$PoPr = [ordered]@{} } $Node = Split-Path -Leaf $file $return.Profiles.$PoPr.$Node = $file } foreach ($key in $keys) { $p = [System.IO.Path]::Combine($Directory,$PoPr,$SubDir,$key) if ([System.IO.Directory]::Exists($p)) { switch -Exact ($key) { Config { # Top directory for PowerProfile specific config only [string[]]$files = [System.IO.Directory]::EnumerateFiles($p,'*.config.json','TopDirectoryOnly') [array]::Sort($files) foreach ($file in $files) { $Node = Split-Path -Leaf $file if( $null -eq $return.'PoProfileConfig'.$PoPr ) { $return.'PoProfileConfig'.$PoPr = [ordered]@{} } $return.'PoProfileConfig'.$PoPr.$Node = $file } # Sub directories for 3rd-party configuration assets [string[]]$dirs = [System.IO.Directory]::EnumerateDirectories($p,'*','TopDirectoryOnly') foreach ($dir in $dirs) { $Node = Split-Path -Leaf $dir if( $null -eq $return.$key.$PoPr ) { $return.$key.$PoPr = @{} } [string[]]$files = [System.IO.Directory]::EnumerateFiles($dir,'*','TopDirectoryOnly') [array]::Sort($files) foreach ($file in $files) { $fNode = Split-Path -Leaf $file if( $null -eq $return.$key.$PoPr.$Node ) { $return.$key.$PoPr.$Node = [ordered]@{} } $return.$key.$PoPr.$Node.$fNode = $file } } Break } Functions { [string[]]$files = [System.IO.Directory]::EnumerateFiles($p,'*.ps1','AllDirectories') [array]::Sort($files) foreach ($file in $files) { if ($file -match '\.Test\.ps1$') { continue } $Node = Split-Path -Leaf $file $return.$key.$Node = $file } Break } Modules { if (@([System.IO.Directory]::EnumerateDirectories($p,'*','TopDirectoryOnly')).Count -gt 0) { $return.$key += $p } Break } Scripts { if (@([System.IO.Directory]::EnumerateFiles($p,'*.ps1','TopDirectoryOnly')).Count -gt 0) { $return.$key += $p } Break } } } } } } } } $return } #endregion #region Functions: Write function Write-PoProfileItemProgress { Param( [string]$ProfileTitle, [string]$ItemTitle, [AllowEmptyString()] [string]$ItemCategory, [string]$ItemText, [string]$ItemTextColor=$PSStyle.Foreground.BrightBlack, [Int]$Depth ) if ($IsCommand -or $IsNonInteractive -or ($null -ne $env:PSLVL)) { return } $Script:PoProfileProgressScriptCategoryShowed = $false $Script:PoProfileProgressScriptTitleShowed = $false if ($PSBoundParameters.ContainsKey('ProfileTitle') -and $ProfileTitle -ne $PoProfileProgressTitle) { if ($PoProfileProgressItemTextShowed) { Write-Host '' } if ($PoProfileProgressItemTitleShowed) { Write-Host '' } $Script:PoProfileProgressTitleShowed = $false $Script:PoProfileProgressItemTitleShowed = $false $Script:PoProfileProgressItemTextShowed = $false $Script:PoProfileProgressDepth = 1 $Script:PoProfileProgressCounter = 1 $Script:PoProfileProgressTitle = $ProfileTitle } if ($PSBoundParameters.ContainsKey('ItemTitle') -and $ItemTitle -ne $PoProfileProgressItemTitle) { if ($PoProfileProgressItemTextShowed) { Write-Host '' } $Script:PoProfileProgressItemTitleShowed = $false $Script:PoProfileProgressItemCategoryShowed = $false $Script:PoProfileProgressItemTextShowed = $false $Script:PoProfileProgressDepth = 1 $Script:PoProfileProgressItemTitle = $ItemTitle } if ($PSBoundParameters.ContainsKey('ItemCategory') -and $ItemCategory -ne $PoProfileProgressItemCategory) { if ($PoProfileProgressItemTextShowed) { Write-Host '' } $Script:PoProfileProgressItemCategoryShowed = $false $Script:PoProfileProgressItemTextShowed = $false $Script:PoProfileProgressItemCategory = $ItemCategory } if ($PSBoundParameters.ContainsKey('Depth') -and $Depth -ge 1 -and $Depth -ne $PoProfileProgressDepth) { $Script:PoProfileProgressDepth = $Depth } if (-not $PSBoundParameters.ContainsKey('ItemText')) { return } if (-Not $PoProfileProgressTitleShowed) { $Script:PoProfileProgressTitleShowed = $true Write-Host ($PSStyle.Foreground.BrightGreen + $PoProfileProgressTitle + ':' + $PSStyle.Reset) } if (-Not $PoProfileProgressItemTitleShowed) { $Script:PoProfileProgressItemTitleShowed = $true $Number = (' ' * (3 - (@($PoProfileProgressCounter.ToString().Length, 3) | Measure-Object -Minimum).Minimum)) + $PoProfileProgressCounter $Script:PoProfileProgressCounter++ Write-Host ($PSStyle.Foreground.BrightWhite + $PSStyle.Bold + "${Number}. ${Script:PoProfileProgressItemTitle}:" + $PSStyle.Reset) } $Indentation = ' ' * (5 + $Script:PoProfileProgressDepth) if (-Not $PoProfileProgressItemCategoryShowed) { $Script:PoProfileProgressItemCategoryShowed = $true if ($PoProfileProgressItemCategory -and '' -ne $PoProfileProgressItemCategory) { $Script:PoProfileProgressDepth++ Write-Host ($Indentation + $PSStyle.Foreground.Yellow + $PoProfileProgressItemCategory + ':' + $PSStyle.Reset) $Indentation = " $Indentation" } } if ($PoProfileProgressItemTextShowed) { $ItemText = " ${ItemText}" } elseif ($PoProfileProgressItemCategoryShowed) { $ItemText = "${Indentation}${ItemText}" } else { $ItemText = "${Indentation} ${ItemText}" } $Script:PoProfileProgressItemTextShowed = $true Write-Host "${ItemTextColor}${ItemText}$($PSStyle.Reset)" -NoNewline } function Write-PoProfileProgress { Param( [string]$ProfileTitle, [AllowEmptyString()] [string]$ScriptCategory, [string[]]$ScriptTitle, [ValidateSet('Progress','Verbose','Note','Confirmation','Information','Warning','Error')] [string]${ScriptTitleType}='Progress', [switch]$NoCounter ) if ($IsCommand -or $IsNonInteractive -or ($null -ne $env:PSLVL)) { return } if ($PoProfileProgressItemTextShowed) { Write-Host '' } $Script:PoProfileProgressItemTitleShowed = $false $Script:PoProfileProgressItemCategoryShowed = $false $Script:PoProfileProgressItemTextShowed = $false if ($PSBoundParameters.ContainsKey('ProfileTitle') -and $ProfileTitle -ne $PoProfileProgressTitle) { if ($PoProfileProgressScriptTitleShowed) { Write-Host '' } $Script:PoProfileProgressTitleShowed = $false $Script:PoProfileProgressScriptTitleShowed = $false $Script:PoProfileProgressDepth = 1 $Script:PoProfileProgressCounter = 1 $Script:PoProfileProgressTitle = $ProfileTitle } if ($PSBoundParameters.ContainsKey('ScriptCategory') -and $ScriptCategory -ne $PoProfileProgressScriptCategory) { $Script:PoProfileProgressScriptCategoryShowed = $false $Script:PoProfileProgressScriptTitleShowed = $false $Script:PoProfileProgressScriptCategory = $ScriptCategory } if (-not $PSBoundParameters.ContainsKey('ScriptTitle')) { return } if (-Not $PoProfileProgressTitleShowed) { $Script:PoProfileProgressTitleShowed = $true Write-Host ($PSStyle.Foreground.BrightGreen + $PoProfileProgressTitle + ':' + $PSStyle.Reset) } if (-Not $PoProfileProgressScriptCategoryShowed) { $Script:PoProfileProgressScriptCategoryShowed = $true if ($PoProfileProgressScriptCategory -and '' -ne $PoProfileProgressScriptCategory) { Write-Host (' ' + $PSStyle.Foreground.Yellow + $PoProfileProgressScriptCategory + ':' + $PSStyle.Reset) } } $Script:PoProfileProgressScriptTitleShowed = $true if ($ScriptTitleType -eq 'Confirmation') { $Color = $PSStyle.Foreground.BrightGreen $ShowSquare = '✔' } elseif ($ScriptTitleType -eq 'Information') { $Color = $PSStyle.Foreground.BrightCyan $ShowSquare = 'i' } elseif ($ScriptTitleType -eq 'Warning') { $Color = $PSStyle.Foreground.BrightYellow $ShowSquare = '!' } elseif ($ScriptTitleType -eq 'Error') { $Color = $PSStyle.Foreground.BrightRed $ShowSquare = 'X' } elseif ($ScriptTitleType -eq 'Note') { $Color = $PSStyle.Foreground.BrightBlue } elseif ($ScriptTitleType -eq 'Verbose') { $Color = $PSStyle.Foreground.BrightBlack } else { $Color = $PSStyle.Foreground.White } if ($ShowSquare) { Write-Host ( " $Color" + $PSStyle.Reverse + " $ShowSquare " + $PSStyle.ReverseOff + $( if ($Color -eq $PSStyle.Foreground.BrightRed) { $Color = $PSStyle.Foreground.Red } $count = 1 $Color + $( foreach ($Line in $ScriptTitle) { if ($count -eq 1) { $Prefix = ' ' } else { $Prefix = ' ' } "${Prefix}${Line}" + [System.Environment]::NewLine $count++ } ) ) + $PSStyle.Reset ) } else { if ($NoCounter) { $Prefix = ' ' $Suffix = '' } else { $Prefix = (' ' * (3 - (@($PoProfileProgressCounter.ToString().Length, 3) | Measure-Object -Minimum).Minimum)) + $PoProfileProgressCounter + '. ' $Script:PoProfileProgressCounter++ $Suffix = ' ...' } Write-Host ( $PSStyle.Bold + "${Prefix}${ScriptTitle}${Suffix}" + $PSStyle.Reset) } } #endregion #region Functions: PowerShell Core function pwsh { [System.Collections.ArrayList]$a = $args if ($IsLogin -and $a[0] -notmatch '(?i)^-L(o(g(in?)?)?)?$') { $a.Insert(0,'-l') } if ($a -notmatch '^-NoL.*') { if ($IsLogin) { $a.Insert(1,'-nol') } else { $a.Insert(0,'-nol') } } (Get-Process -Id $PID).Path + " $a" | Invoke-Expression } Set-Alias -Name pwsh-preview -Value pwsh #endregion #region Functions: PowerShellGet function Invoke-PoProfileInstallModule { [Alias('Install-Module')] [CmdletBinding(DefaultParameterSetName='NameParameterSet', SupportsShouldProcess=$true, ConfirmImpact='Medium', HelpUri='https://go.microsoft.com/fwlink/?LinkID=398573')] param( [Parameter(ParameterSetName='NameParameterSet', Mandatory=$true, Position=0, ValueFromPipelineByPropertyName=$true)] [ValidateNotNullOrEmpty()] [string[]] ${Name}, [Parameter(ParameterSetName='InputObject', Mandatory=$true, Position=0, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] [ValidateNotNull()] [psobject[]] ${InputObject}, [Parameter(ParameterSetName='NameParameterSet', ValueFromPipelineByPropertyName=$true)] [ValidateNotNull()] [string] ${MinimumVersion}, [Parameter(ParameterSetName='NameParameterSet', ValueFromPipelineByPropertyName=$true)] [ValidateNotNull()] [string] ${MaximumVersion}, [Parameter(ParameterSetName='NameParameterSet', ValueFromPipelineByPropertyName=$true)] [ValidateNotNull()] [string] ${RequiredVersion}, [Parameter(ParameterSetName='NameParameterSet')] [ValidateNotNullOrEmpty()] [string[]] ${Repository}, [Parameter(ValueFromPipelineByPropertyName=$true)] [pscredential] [System.Management.Automation.CredentialAttribute()] ${Credential}, [ValidateSet('CurrentUser','AllUsers')] [string] ${Scope}, [Parameter(ValueFromPipelineByPropertyName=$true)] [ValidateNotNullOrEmpty()] [uri] ${Proxy}, [Parameter(ValueFromPipelineByPropertyName=$true)] [pscredential] [System.Management.Automation.CredentialAttribute()] ${ProxyCredential}, [switch] ${AllowClobber}, [switch] ${SkipPublisherCheck}, [switch] ${Force}, [Parameter(ParameterSetName='NameParameterSet')] [switch] ${AllowPrerelease}, [switch] ${AcceptLicense}, [switch] ${PassThru}) begin { try { if ( (-Not $PSBoundParameters.ContainsKey('Scope')) -and $env:IsProfileRedirected ) { $PSBoundParameters['Scope'] = 'AllUsers' } # Implicitly use PSGallery if there are many source repositories # for the same module name $PSRepos = Find-Module $PSBoundParameters['Name'] if ( (-Not $PSBoundParameters.ContainsKey('Repository')) -and ($PSRepos.Count -gt 1) -and ($PSRepos.Repository -contains 'PSGallery') ) { $PSBoundParameters['Repository'] = 'PSGallery' } $outBuffer = $null if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) { $PSBoundParameters['OutBuffer'] = 1 } $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Install-Module', [System.Management.Automation.CommandTypes]::Function) $scriptCmd = {& $wrappedCmd @PSBoundParameters } $steppablePipeline = $scriptCmd.GetSteppablePipeline() $steppablePipeline.Begin($PSCmdlet) } catch { throw } } process { try { $steppablePipeline.Process($_) } catch { throw } } end { try { $steppablePipeline.End() } catch { throw } } <# .ForwardHelpTargetName Install-Module .ForwardHelpCategory Function #> } #endregion #region System Environment switch -Regex (@([System.Environment]::GetCommandLineArgs())) { '(?i)^-C(o(m(m(a(nd?)?)?)?)?)?$' { $Parameters = @{ Scope = 'Script' Name = 'IsCommand' Value = $true Option = 'ReadOnly' Description = 'PowerShell was started with command line argument -Command' } Set-Variable @Parameters continue } '(?i)^-L(o(g(in?)?)?)?$' { $Parameters = @{ Scope = 'Script' Name = 'IsLogin' Value = $true Option = 'ReadOnly' Description = 'PowerShell was started as a login shell' } Set-Variable @Parameters continue } '(?i)^-NoE(x(it?)?)?$' { $Parameters = @{ Scope = 'Script' Name = 'IsNoExit' Value = $true Option = 'ReadOnly' Description = 'PowerShell was started with command line argument -NoExit' } Set-Variable @Parameters continue } '(?i)^-NonI(n(t(e(r(a(c(t(i(ve?)?)?)?)?)?)?)?)?)?$' { $Parameters = @{ Scope = 'Script' Name = 'IsNonInteractive' Value = $true Option = 'ReadOnly' Description = 'PowerShell was explicitly started in non-interactive mode' } Set-Variable @Parameters continue } } if (-Not $IsNonInteractive -and -not [System.Environment]::UserInteractive) { $Parameters = @{ Scope = 'Script' Name = 'IsNonInteractive' Value = $true Option = 'ReadOnly' Description = 'PowerShell is running non-interactive mode' } Set-Variable @Parameters } # env:SHLVL + env:PSLVL $PPID = (Get-Process -Id $PID).Parent.Id if ($PPID) { $ParentProcessName = (Get-Process -Id $PPID -ErrorAction Ignore).ProcessName if ($ParentProcessName -match '(?i)^-?((pwsh(?:-preview)?|powershell)|bash|zsh|sh|csh|dash|ksh|tcsh).*$') { if ($null -ne $matches[2] ) { if($env:PSLVL) { $env:PSLVL = [int]$env:PSLVL + 1 } else { $env:PSLVL = 1 } } if($env:SHLVL) { $env:SHLVL = [int]$env:SHLVL + 1 } else { $env:SHLVL = 1 } } else { $env:SHLVL = 0 } } else { $env:SHLVL = 0 } $PROFILEHOME = Split-Path $PROFILE $env:PSHOST_PROGRAM = (Split-Path -LeafBase $PROFILE.CurrentUserCurrentHost).Replace('Microsoft.','') -replace '_profile$' $env:LC_PSHOST = $( if ($env:PSHOST_PROGRAM -eq 'PowerShell') { if ($IsCoreCLR) { 'PowerShell' } else { 'Windows PowerShell' } } else { (($env:PSHOST_PROGRAM -replace '(?-i)([a-z]{3,})([A-Z])','$1 $2') -replace '_',' ').Trim() } ) if ($null -eq $env:PSLVL) { if ($IsWindows) { $PSType = if ($IsCoreCLR) { 'PowerShell' } else { 'WindowsPowerShell' } $PSMyDocumentsPath = [System.IO.Path]::Combine(([System.Environment]::GetFolderPath('MyDocuments')),$PSType) # $PSProgramFilesPath = [System.IO.Path]::Combine(([System.Environment]::GetFolderPath('ProgramFiles')),$PSType) $env:HOSTNAME = $env:COMPUTERNAME $env:XDG_DATA_HOME = [System.Environment]::GetFolderPath('LocalApplicationData') $env:XDG_CONFIG_HOME = [System.Environment]::GetFolderPath('ApplicationData') $env:XDG_STATE_HOME = [System.IO.Path]::Combine($env:XDG_DATA_HOME,'.State') $WindowsPrincipal = [Security.Principal.WindowsPrincipal]::new([Security.Principal.WindowsIdentity]::GetCurrent()) if ($WindowsPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) -eq 1) { $env:IsElevated = $true } if ($env:PSModulePath.Contains($env:OneDrive) -or $env:PSModulePath.Contains($env:OneDriveCommercial)) { $env:IsProfileRedirected = $true } } else { if ($env:SHLVL -eq 0) { if ($IsMacOS) { if ( -Not $IsLogin -and ([System.IO.File]::Exists('/usr/libexec/path_helper')) ) { function setenv ($n,$v) {[System.Environment]::SetEnvironmentVariable($n,$v)} /usr/libexec/path_helper -c | Invoke-Expression -ErrorAction Ignore } # Homebrew support $( if ([System.IO.File]::Exists('/opt/homebrew/bin/brew')) { /opt/homebrew/bin/brew shellenv } elseif ([System.IO.File]::Exists('/usr/local/bin/brew')) { /usr/local/bin/brew shellenv } ) | Invoke-Expression -ErrorAction Ignore } else { # Homebrew on Linux support $( if ([System.IO.File]::Exists("$HOME/.linuxbrew/bin/brew")) { "$HOME/.linuxbrew/bin/brew shellenv" } elseif ([System.IO.File]::Exists('/home/linuxbrew/.linuxbrew/bin/brew')) { '/home/linuxbrew/.linuxbrew/bin/brew shellenv' } ) | Invoke-Expression -ErrorAction Ignore } } $PSMyDocumentsPath = [System.IO.Path]::Combine(([System.Environment]::GetFolderPath('LocalApplicationData')),'powershell') # $PSProgramFilesPath = '/usr/local/share/powershell' $env:PROCESSOR_ARCHITECTURE = $(uname -m).ToUpper() | ForEach-Object { if ($_ -eq 'X86_64') {'AMD64'} else {$_} } $env:COMPUTERNAME = $(hostname -s).ToUpper() $env:HOSTNAME = $env:COMPUTERNAME $env:XDG_DATA_HOME = [System.IO.Path]::Combine(([System.Environment]::GetFolderPath('UserProfile')),'.local','share') $env:XDG_CONFIG_HOME = [System.IO.Path]::Combine(([System.Environment]::GetFolderPath('UserProfile')),'.config') $env:XDG_STATE_HOME = [System.IO.Path]::Combine(([System.Environment]::GetFolderPath('UserProfile')),'.local','state') if (0 -eq (id -u)) { $env:IsElevated = $true } # env:PSModulePath $env:PSModulePath += [System.IO.Path]::PathSeparator + [System.IO.Path]::Combine($PROFILEHOME,'Modules') } # env:TERM_PROGRAM if (-Not $env:TERM_PROGRAM) { $TerminalName = $ParentProcessName -replace '(?:\(|Server)?(-.*)?$' if ($env:LC_TERMINAL) { $env:TERM_PROGRAM = $env:LC_TERMINAL } elseif ( ($env:PSHOST_PROGRAM -ne 'PowerShell') -and ($env:PSHOST_PROGRAM -ne 'WindowsPowerShell') ) { $env:TERM_PROGRAM = $env:PSHOST_PROGRAM } elseif ( $TerminalName -eq 'WindowsTerminal' ) { $env:TERM_PROGRAM = $TerminalName } } if ($env:TERM_PROGRAM -and -not $env:LC_TERMINAL) { $env:LC_TERMINAL = (($env:TERM_PROGRAM -replace '(?-i)([a-z]{3,})([A-Z])','$1 $2') -replace '_',' ').Trim() } # Add Scripts directories to env:PATH $p = [System.IO.Path]::Combine($PSMyDocumentsPath,'Scripts') if ([System.IO.Directory]::Exists($p)) { $env:PATH += [System.IO.Path]::PathSeparator + $p } if ($PSMyDocumentsPath -ne $PROFILEHOME) { $p = [System.IO.Path]::Combine($PROFILEHOME,'Scripts') if ([System.IO.Directory]::Exists($p)) { $env:PATH += [System.IO.Path]::PathSeparator + $p } } } #endregion Get-PoProfileContent |