# Specify if the change should apply to the CURRENT USER only, or to ALL users.
# NOTE: If you set this to $true - which is NOT ADVISABLE -
# you'll need to run this code ELEVATED (as administrator)
$forAllUsers = $false
# Determine the chosen scope's target registry key path.
$targetKey = "$(('HKCU', 'HKLM')[$forAllUsers]):\Software\Classes\Microsoft.PowerShellScript.1\shell\Open\Command"
# In the user-specific hive (HKCU: == HKEY_CURRENT_USER), the target key
# doesn't exist by default (whereas it does in the local-machine hive (HLKM: == HKEY_LOCAL_MACHINE)),
# so we need to make sure that it exists.
if (-not $forAllUsers -and -not (Test-Path -LiteralPath $targetKey)) {
$null = New-Item -Path $targetKey -Force -ErrorAction Stop
}
# Specify the command to use when opening / double-clicking *.ps1 scripts:
# As written here:
# * The script runs in the directory in which it resides.
# * The profiles are loaded (add -NoProfile to change).
# * The current execution policy is respected (add -ExecutionPolicy <policy> to override, if possible)
# * The window stays open after the script exits (remove -NoExit to change)
# For help with all parameters, see https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_powershell_exe
$cmd = "`"$((Get-Process -Id $PID).Path)`" -nologo -noexit -file `"%1`" %*"
# Write the command to the registry.
Set-ItemProperty -ErrorAction Stop -LiteralPath $targetKey -Name '(default)' -Value $cmd
Write-Verbose -Verbose "$(('User-level', 'Machine-level')[$forAllUsers]) file-type definition for *.ps1 files successfully updated."
# Additionally, make sure that NO OVERRIDES preempt the new definition.
# See if a user override established interactively via File Explorer happens to be defined,
# and remove it, if so.
if ($fileExplorerOverrideKey = Get-Item -ErrorAction Ignore -LiteralPath 'registry::HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.ps1\UserChoice') {
Write-Verbose -Verbose 'Removing File Explorer override...'
# Get the parent key path and the key name
$parentKeyPath = $fileExplorerOverrideKey.PSParentPath -replace '^.+?::\w+\\' # Remove the 'Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\' prefix
$keyName = $fileExplorerOverrideKey.PSChildName
$key = $null
try {
# Open the *parent* key for writing.
$key = [Microsoft.Win32.Registry]::CurrentUser.OpenSubkey($parentKeyPath, $true)
# Delete the subkey.
# !! Due to the specific permissions assigned by File Explorer to the key
# !! (an additional DENY access-control entry for the current user, for the key itself only, for the 'Set Value' permission),
# !! using the .DeleteSubKey*Tree*() method fails (Remove-Item implicitly uses this method and therefore fails too)
# !! However, since there should be no nested subkeys, using .DeleteSubkey() should work fine.
$key.DeleteSubKey($keyName)
}
catch {
throw
}
finally {
if ($key) { $key.Close()}
}
}
# See if *Visual Studio Code* was most recently used to open a *.ps1 file:
# If so, it inexplicably OVERRIDES a file-type definition.
# (This doesn't seem to happen with other executables.)
# !! We fix the problem, but it will RESURFACE the next time File Explorer's shortcut menu
# !! is used to open a *.ps1 file in Visual Studio Code.
if ($itm = Get-Item -ErrorAction Ignore -LiteralPath 'registry::HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.ps1\OpenWithList') {
if (($names = $itm.GetValueNames().Where({ $itm.GetValue($_) -ceq 'Code.exe' })) -and ($mruList = $itm.GetValue('MRUList')) -and $mruList[0] -in $names) {
Write-Warning "Visual Studio Code was most recently used to open a .ps1 file, which unexpectedly overrides the file-type definition.`nCorrecting the problem for now, but it will resurface the next time you use File Explorer's shortcut menu to open a .ps1 file in Visual Studio Code."
# Note: Normally there aren't, but there *can* be *multiple* Code.exe entries, namely after manual removal of the MRUList:
# The next time you choose to open in VSCode via File Explorer's shortcut menu, an *additional* Code.exe entry is added.
do { # Trim the start of the MRUList until its first entry no longer references Code.exe
$mruList = $mruList.Substring(1)
} while ($mruList[0] -in $names)
# Update the MRUList value in the registry.
$itm | Set-ItemProperty -Name 'MRUList' -Value $mruList
}
}
4条答案
按热度按时间jdgnovmf1#
根据设计,双击(打开)
*.ps1
文件从Windows [GUI] shell(本例中:桌面,文件资源管理器和任务栏,通过固定的项目)不会执行它们-相反,它们会在记事本或PowerShell伊势中打开以进行编辑,具体取决于Windows / PowerShell版本。但是,至少从Windows 7开始,
*.ps1
文件的快捷菜单包含**Run with PowerShell
命令**,该命令 * 确实 * 调用手头的脚本;这 * 可能 * 对你的目的来说已经足够了,但是这个调用方法有限制-详情请看下面的部分。如果你确实想*重新定义 * 双击/打开,让它 * 执行 *
*.ps1
脚本,有两个选择:注意事项:
对于 * 给定 * 脚本(而不是 * 所有 *
.ps1
文件),您可以选择创建一个 * 快捷方式文件 * 或 * 批处理文件 * 来启动它,但不是一般的解决方案,因为您必须通过双击为您想要运行的每个.ps1
文件创建一个配套文件。但是,它确实给予您能够完全控制调用。您可以通过文件资源管理器交互式 * 创建快捷方式文件,如this answer中所述,或 * 以编程方式 * 创建快捷方式文件,如this answer中所示。同样,您可以创建一个配套的 * 批处理文件*(.cmd
或.bat
)来调用您的脚本,因为批处理文件 * 在双击时 * 执行;例如,如果您将一个与.ps1
脚本具有相同基本名称的批处理文件放在同一目录中(e.例如,foo.cmd
紧挨着foo.ps1
),您可以从批处理文件调用它,如下所示;-NoExit
保持会话打开:-File
之前放置一个-ExecutionPolicy Bypass
参数。cmd.exe
控制台窗口*中直接同步执行.ps1
脚本 *。换句话说:您可以直接执行脚本foo.ps1
,而不必使用PowerShell CLI,比如powershell.exe -File foo.ps1
[* 不推荐 *] GUI 方法:
使用文件资源管理器使PowerShell默认执行
.ps1
文件:.ps1
文件并选择Properties
。Opens with:
标签旁边的Change...
。More apps
并向下滚动到Look for another app on this PC
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
并提交。此方法无法控制PowerShell调用的具体细节,且有主要限制;实际上,您将以以下行为结束:
*主要限制:
*脚本路径 * 带有嵌入空格 * 和
'
字符。不能以这种方式调用,因为,即使这样的路径是用双引号传递的,后者实际上被PowerShell * 剥离 *,因为路径被传递给 implied-Command
参数,其首先剥离在将结果 * 解释为PowerShell代码 * 之前,命令行中的(非转义)双引号-在这种情况下,带空格的路径被视为 * 多个 * 参数/包含(奇数个)'
的路径导致语法错误。pwsh.exe
,即跨平台按需安装PowerShell (Core) 7+ edition的CLI,则该问题 * 不会 * 出现,因为它默认为-File
参数-在这种情况下,双引号脚本文件路径 * 被 * 正确识别。-Command
与使用-File
的,参见this answer。*不支持传递参数,如果您想直接 * 从
cmd.exe
* 调用.ps1
文件,并且需要传递参数,这一点很重要。Restricted
有效,则调用将完全失败。Run in PowerShell
命令一样,脚本运行的窗口将在脚本结束时自动关闭 * -因此,除非脚本在退出前明确提示用户,否则您可能无法检查其输出。要对PowerShell调用脚本的方式进行更多控制,包括支持带空格的路径 * 和 * 传递参数*,请使用下一节中所示的编程方法。
* 编程 * 方法:
重要:
*GUI方法 * 覆盖 * 编程解决方案,因此必须将其删除-下面的代码将 * 自动 * 删除。
.ps1
文件时,它会重新出现**。修改注册表,重新定义
HKEY_CLASSES_ROOT\Microsoft.PowerShellScript.1\shell\Open\Command
下*.ps1
文件的Open
快捷菜单命令,如下图所示。您可以按原样运行代码,创建一个用户级文件类型定义,该文件类型定义:
powershell.exe
和 PowerShell(Core)7+ 中的pwsh.exe
。-ExecutionPolicy
参数以覆盖。-NoProfile
以抑制加载;如果您计划直接从cmd.exe
* 调用.ps1
文件 *,而不是(仅)从文件资源管理器,并结合使用-NoExit
not,则这将非常有用。-NoExit
以在脚本结束时退出会话;如果您计划直接从cmd.exe
* 调用.ps1
文件 *,而不是(仅)从文件资源管理器调用,那么这将非常有用。如果您的要求不同-如果您需要不同的CLI parameters和/或您想使用
pwsh.exe
,i.e. PowerShell(Core)7+ 代替-首先通过修改下面的$cmd = ...
行来调整代码;看上面的评论。**预定义 *
Run in PowerShell
快捷菜单命令说明 *:它在注册表项
HKEY_CLASSES_ROOT\Microsoft.PowerShellScript.1\shell\0\Command
(从Windows 10开始)中定义如下:'
字符的脚本文件路径断开。除非执行策略
AllSigned
生效-这种情况下,只有 * 签名 * 的脚本才能执行,但执行时 * 没有提示 * -该命令尝试将被调用进程的执行策略设置为Bypass
,这意味着任何 * 脚本都可以执行。但仅在用户预先响应 * 确认提示之后(不管脚本是否签名,以及是否从web下载)。***除非目标脚本在退出前显式暂停以等待用户输入,否则脚本完成时脚本将 * 自动关闭**的窗口,因此您可能无法看到其输出。
[1]较早的,被破坏的命令定义是
"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" "-file" "%1" "-Command" "if((Get-ExecutionPolicy ) -ne AllSigned) { Set-ExecutionPolicy -Scope Process Bypass }"
,这意味着-file "%1"
之后的任何内容都作为 arguments 传递到文件"%1"
,而不是-Command
之后的命令的预期执行;另外-一个有争议的问题-AllSigned
操作数将需要被引用。wlwcrazw2#
双击执行PS1文件(运行)
为文件创建快捷方式并将目标设置为:
用脚本的位置替换第二个目录(引号中的那个)。
双击读取PS1文件(编辑)
同上,但以伊势为目标,因为这将强制其进入编辑模式。
14ifxucb3#
默认情况下,Server 2012和更新版本不关联。PS1文件扩展名与PowerShell可执行文件;而是默认为打开。PS1文件与记事本默认为安全原因。
如果您有访问权限,则需要通过控制面板中的“默认程序”更改文件关联。PS1文件执行双击。
还要注意,您可能必须更改执行策略,才能运行特定的脚本。
此外,听起来这个脚本可能是一个核心自动化,您可以使用其中任何一个从另一个脚本中执行脚本,而无需更改活动工作目录:调用项“”&"
alen0pnh4#
我已经修复了注册表值,以便ps1脚本可以通过双击或使用“使用PowerShell运行”从任何位置执行,即使路径中有多个连续空格和撇号: