PowerShell$配置文件、变量和NoteProperty/添加成员

PowerShell$配置文件、变量和NoteProperty/添加成员,powershell,console,profile,Powershell,Console,Profile,$profile让我有些头疼$Profile.GetType()解析为String,这很好,但它有NoteProperty值: $profile | Get-Member -Type NoteProperty AllUsersAllHosts、AllUsersCurrentHosts、CurrentUserAllHosts、CurrentUserCurrentHost 当我键入$profile时,将返回CurrentUserCurrentHost NoteProperty。这很好,但我需要更改

$profile
让我有些头疼
$Profile.GetType()
解析为
String
,这很好,但它有NoteProperty值:

$profile | Get-Member -Type NoteProperty
AllUsersAllHosts、AllUsersCurrentHosts、CurrentUserAllHosts、CurrentUserCurrentHost

当我键入
$profile
时,将返回CurrentUserCurrentHost NoteProperty。这很好,但我需要更改此值-这很复杂,但我的公司VPN使用网络配置文件,当我在线时,它会尝试为我的
$profile
引用该位置,这意味着每次控制台启动都需要9秒(非常慢)。如果在本地加载配置文件,我可以将时间缩短到1秒,但这意味着要更改这些值。为此,我将以下内容放入
AllUsersAllHosts
profile.ps1

$profile = C:\Users\($env:Username)\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1"
这很好,但是通过这样做,我发现所有的NoteProperty值都被删除了!所以我试着:

$profile.CurrentUserCurrentHost = C:\Users\($env:Username)\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1"
但这失败了,因为$Profile的根值继续指向网络配置文件,控制台启动时间又是9秒

然后我也注意到了以下奇怪之处:

$x = [string]$profile
$x -eq $profile
主要问题有:

•为什么$x返回
True
,即使$x在
$profile
中没有
NoteProperty
值(因为对象肯定不一样!)

•如何控制
$profile
的根值,而不破坏
NoteProperty
值,以及

•如何更新
.CurrentUserAllHosts
.CurrentUserCurrentHost
,使根值也相应更新?i、 e.即使我执行以下操作,
$profile
的根值保持不变(仍然指向非常慢的网络配置文件位置):


虽然从技术上讲,可以将NoteProperty成员从一个字符串实例复制到另一个实例(请参见下文),但不幸的是,这无助于您:

PowerShell为用户的利益公开了
$PROFILE
变量:

  • 它不会在内部使用该变量的值来确定加载配置文件时的配置文件位置
  • 这些配置文件位置不可配置
您应该将
$PROFILE
视为只读变量
,即使它在技术上可以修改

有关解决方法的信息,请参阅的答案

如果重点是启动交互式会话,则最有希望的方法是创建一个专用的快捷方式文件(
*.lnk
),用于启动PowerShell会话,该文件由以下目标命令定义:

powershell -noprofile -noexit -c ". c:\CustomProfileDir\profile.ps1"
要使用PowerShell[Core]6+,请将
pwsh
替换为
PowerShell


在对象之间复制NoteProperty成员: 注:

  • 如上所述,这不会解决您的问题,但它说明了如何将NoteProperty成员从一个对象复制到另一个对象,即使是在具有不同值的字符串之间

  • 具体而言,对于
    [string]
    实例,需要使用
    -PassThru
    并将其赋值回输入变量(
    $obj=$obj | Add Member-PassThru…
    ),以便保留NoteProperty成员;对于其他类型,
    $obj |添加成员…
    就足够了


正如@mklement0所指出的,我不能绕过$profile的功能,但我可以欺骗它。如果没有以下内容,当我远程连接到我的公司VPN时,所有shell和脚本需要6.5到9秒才能启动(其中一部分,大约3秒是我的
$profile
功能等)。使用下面的命令,所有控制台和脚本都会在1秒内启动(包括我的
$profile
功能,大约1000行代码)。将此解决方案放在这里,以防对其他人有所帮助

运行此答案底部的代码会将以下内容推入my
$Profile.AllUsersAllHosts
,它现在将始终引用默认C:\位置的配置文件(并且正确地根据主机类型-console、ISE、VSCode等)

然后我需要做的就是删除网络共享上的任何配置文件,现在无论是否远程连接到VPN,无论我打开控制台还是脚本等,我都可以获得1秒的加载时间

Write-Host ""
Write-Host "Do you want to update `$Profile.AllUsersAllHosts to always redirect to C:\Users\$($env:Username) ?"
Write-Host "This can significantly speed up PowerShell load times when working with a network share over a VPN."
Write-Host "Note that this is just a bypass to always use the *default* profile location on C:\."
Write-Host "   `$Profile.AllUsersAllHosts = C:\Windows\System32\WindowsPowerShell\v1.0\profile.ps1"
Write-Host "   `$Profile.CurrentHostCurrentUser = C:\Users\$($env:Username)\Documents\WindowsPowerShell\$(($Profile).split("\"))[-1]_profile.ps1"
Write-Host ""

$ProfileAdmin = "C:\Windows\System32\WindowsPowerShell\v1.0\profile.ps1"
# Use  (($Profile).split("\"))[-1]  to capture correct prefix PowerShell, VSCode. $ShellId does not work for VSCode etc
$ProfileUser = "C:\Users\$($env:Username)\Documents\WindowsPowerShell\$(($Profile).split('\')[-1])"
$ProfileUserText = '"C:\Users\$($env:Username)\Documents\WindowsPowerShell\$(($Profile).split(`"\`")[-1])"'

Write-Host "Press any key to use `$Profile.AllUsersAllHosts to stop using a profile from a network share"
pause
# - Create $Profile.AllUsersAllHosts
# - Set-Location C:\Users\$($env:Username)   (override Admin defaulting into system32)
# - If the system decides that it will use C:\ have to check that so that we do not double-load $Profile!
# - If that condition is ok, set $Profile = "C:\Users\$($env:Username)\Documents\WindowsPowerShell\$($ShellId)_profile.ps1"
# - Then just dotsource $Profile as it's set to the C:\ drive as required.
if (!(Test-Path $(Split-Path $ProfileAdmin))) { mkdir $(Split-Path $ProfileAdmin) -Force }
if (!(Test-Path $(Split-Path $ProfileUser))) { mkdir $(Split-Path $ProfileUser) -Force }
Write-Host "`nCreating backup of existing profile ..."
If (Test-Path $ProfileAdmin) { Copy-Item -Path "$($ProfileAdmin)" -Destination "$($ProfileAdmin)_$(Get-Date -format "yyyy-MM-dd__HH-mm-ss").txt" }
Set-Content -Path $ProfileAdmin -Value "Set-Location C:\Users\`$(`$env:Username)"
Add-Content -Path $ProfileAdmin -Value ""
Add-Content -Path $ProfileAdmin -Value "# Prevent loading profile from network share"
Add-Content -Path $ProfileAdmin -Value "if (`$Profile.CurrentUserCurrentHost -ne $ProfileUserText) {"
Add-Content -Path $ProfileAdmin -Value "    `$Profile = $ProfileUserText"
Add-Content -Path $ProfileAdmin -Value "    if (Test-Path `"`$Profile`") { . `$Profile }"
Add-Content -Path $ProfileAdmin -Value "}"

好的,谢谢。非常有趣。我想那时我所能做的就是像我那样吹走
$profile
。只要重新分配
$profile=C:\new\path.ps1
,我就有问题了。AllUsersAllHosts将为所有主机(如ISE或VSCode)加载。你知道如何通过查询PowerShell来获得正确的名称吗<代码>主机不好,因为它不返回控制台的Microsoft.PowerShell\u profile.ps1,也不返回VSCode的Microsoft.VSCode\u profile.ps1。我想知道PowerShell是如何获得Microsoft.VSCode\u profile.ps1的。如果我能做到这一点,那么我就可以提取我需要的东西。有什么好主意吗@YorSubs,重点是,虽然从技术上讲,您可以按照自己想要的方式定义
$PROFILE
(如答案的底部部分所示),但PowerShell将忽略重新定义。
Microsoft.VSCode
部分来自自动
$ShellId
变量。底线是:您无法更改PowerShell查找配置文件的位置,但您可以使用链接答案中的技术来解决这一问题。您是对的。我以为它是按照我想要的方式运行的,但这只是巧合,因为我使用的是路径。可怕的是MS不允许永久更改配置文件位置。安全问题不能成为理由(因为Windows只是安全问题的一个句号!)。好的,我想我要做的是让位置删除所有网络路径位置中的所有配置文件,只从
AllUsersAllHosts
中删除dotsource。真的很低效,因为我需要管理员,不能用用户上下文控制一切
# Assign the new string value to a new (temporary) variable.
$newProfile = '/path/to/my.ps1'

# Copy the NoteProperty members from the original, decorated string
# to the new variable's string instance.
$PROFILE.psobject.properties | where MemberType -eq 'NoteProperty' | foreach { 
  $newProfile = $newProfile |
    Add-Member -PassThru -NotePropertyName $_.Name -NotePropertyValue $_.Value  
}

# You can now assign the new variable to the old one,
# but note that *with $PROFILE that won't help*.
# $PROFILE SHOULD BE TREATED AS READ-ONLY.
$PROFILE = $newProfile 
Set-Location C:\Users\$($env:Username)

# Prevent loading profile from network share
if ($Profile.CurrentUserCurrentHost -ne "C:\Users\$($env:Username)\Documents\WindowsPowerShell\$(($Profile).split(`"\`")[-1])") {
    $Profile = "C:\Users\$($env:Username)\Documents\WindowsPowerShell\$(($Profile).split(`"\`")[-1])"
    if (Test-Path "$Profile") { . $Profile }
}
Write-Host ""
Write-Host "Do you want to update `$Profile.AllUsersAllHosts to always redirect to C:\Users\$($env:Username) ?"
Write-Host "This can significantly speed up PowerShell load times when working with a network share over a VPN."
Write-Host "Note that this is just a bypass to always use the *default* profile location on C:\."
Write-Host "   `$Profile.AllUsersAllHosts = C:\Windows\System32\WindowsPowerShell\v1.0\profile.ps1"
Write-Host "   `$Profile.CurrentHostCurrentUser = C:\Users\$($env:Username)\Documents\WindowsPowerShell\$(($Profile).split("\"))[-1]_profile.ps1"
Write-Host ""

$ProfileAdmin = "C:\Windows\System32\WindowsPowerShell\v1.0\profile.ps1"
# Use  (($Profile).split("\"))[-1]  to capture correct prefix PowerShell, VSCode. $ShellId does not work for VSCode etc
$ProfileUser = "C:\Users\$($env:Username)\Documents\WindowsPowerShell\$(($Profile).split('\')[-1])"
$ProfileUserText = '"C:\Users\$($env:Username)\Documents\WindowsPowerShell\$(($Profile).split(`"\`")[-1])"'

Write-Host "Press any key to use `$Profile.AllUsersAllHosts to stop using a profile from a network share"
pause
# - Create $Profile.AllUsersAllHosts
# - Set-Location C:\Users\$($env:Username)   (override Admin defaulting into system32)
# - If the system decides that it will use C:\ have to check that so that we do not double-load $Profile!
# - If that condition is ok, set $Profile = "C:\Users\$($env:Username)\Documents\WindowsPowerShell\$($ShellId)_profile.ps1"
# - Then just dotsource $Profile as it's set to the C:\ drive as required.
if (!(Test-Path $(Split-Path $ProfileAdmin))) { mkdir $(Split-Path $ProfileAdmin) -Force }
if (!(Test-Path $(Split-Path $ProfileUser))) { mkdir $(Split-Path $ProfileUser) -Force }
Write-Host "`nCreating backup of existing profile ..."
If (Test-Path $ProfileAdmin) { Copy-Item -Path "$($ProfileAdmin)" -Destination "$($ProfileAdmin)_$(Get-Date -format "yyyy-MM-dd__HH-mm-ss").txt" }
Set-Content -Path $ProfileAdmin -Value "Set-Location C:\Users\`$(`$env:Username)"
Add-Content -Path $ProfileAdmin -Value ""
Add-Content -Path $ProfileAdmin -Value "# Prevent loading profile from network share"
Add-Content -Path $ProfileAdmin -Value "if (`$Profile.CurrentUserCurrentHost -ne $ProfileUserText) {"
Add-Content -Path $ProfileAdmin -Value "    `$Profile = $ProfileUserText"
Add-Content -Path $ProfileAdmin -Value "    if (Test-Path `"`$Profile`") { . `$Profile }"
Add-Content -Path $ProfileAdmin -Value "}"