如何基于python hashbang从powershell运行python程序?

我有几个 python3 脚本使用hashbang将其标识为 Python3 脚本。无法根据文件扩展名将脚本识别为 python,因为它们没有(就像在任何其他 *nix 系统中一样)。


我已经查看了相关问题,但它们并没有解决这个特殊的需求,而且似乎依赖于*.py扩展来获得认可。


在 Windows 上设置 Python 以不在 cmd 中键入“python”

如何使 python 脚本在 Windows 上可执行?

所以我的脚本被命名为:myscript,文件的第一行是:


#!/usr/bin/env python3

...

如何让 Windows powershell识别这一点并使用位于 中的 python 解释器运行它C:\Python3.7?


更新-1


澄清一下,我想从powershell CLI运行它,而不是通过单击它。此外,我刚刚发现(令我震惊)当您使用pip install本机 Windows Python3 时,第一行hashbang会自动替换为:


#!c:\python37\python.exe


哎哟!


更新-2


感谢@eryksun 的评论,我设法获得了一个 PowerShell 脚本来为我做一些基本的检查。但是,需要对其进行修复以支持更多 Python。


test4script.ps1:



Param([parameter(Mandatory=$true, HelpMessage="Need a valid filename")] $fileName)

$firstLine = Get-Content -Path $fileName -TotalCount 1

$SHEBANG="^#!"

$shes=@("python3","python2","python","bash","sh","perl","pwsh")


If ($firstLine -match $SHEBANG) {

    Write-Host "DEBUG: checking for common shebangs" -ForegroundColor Green

    foreach ($i in $shes) {

        If ($firstLine -match $i) {

            Write-Host "DEBUG: found shebang for: $i" -ForegroundColor Green

            C:\python37\python.exe $fileName

            break

        }

    }

} else {

    Write-Host "File is not a known script. No shebang fund in file!" -ForegroundColor Red

    return

}

Write-Host "DEBUG: Done" -ForegroundColor Green

结果是:


$ Get-Content -Path nonscript -TotalCount 3

#This aint right

echo "hello"


$ Get-Content -Path pip-describe -TotalCount 3

#!c:\python37\python.exe

# pip-describe - Show full text package description from PyPI

# -*- coding: utf-8 -*-


$ .\test4script.ps1 nonscript

File is not a known script. No shebang fund in file!


$ .\test4script.ps1 pip-describe

DEBUG: checking for common shebangs

DEBUG: found shebang for: python3


 Usage:  pip-describe <package-name>


 This will return the full-text package description (usually the README)

 as found on PyPI, for any given <package-name>.

 ...


现在我们应该能够将此脚本与 相关联.,使用CMD.exe与:


cmd /c assoc .=unknown

cmd /c "ftype unknown=""C:\mybin\test4script.ps1"" %1"

但是,使用 PowerShell 独立执行此操作并且不必跳过 CMD 会更好。


芜湖不芜
浏览 193回答 1
1回答

aluckdog

eryksun在对该问题的评论中提供了很好的指示,您基于它们的编辑显示了安装通用的、机器范围的 shebang-line-aware 启动器的方法,用于通过添加.到$env:PATHEXT.这种方法的注意事项:PowerShell 当前(从 PowerShell Core 6.2.0 开始)总是在新的控制台窗口中执行无扩展名的文件,这使得此配置在 PowerShell 中毫无用处——不过,它确实按预期工作cmd.exe。PowerShell 的行为应被视为一个错误,并已在此 GitHub 问题 中报告。该机制存在潜在的安全风险,因为任何具有 shebang 行的没有扩展名的纯文本文件实际上都可以执行,从而可能绕过专注于具有已知可执行扩展名的文件的安全功能。通过 [PowerShell] 脚本实现文件类型定义的默认操作总是需要使用脚本文件的解释器创建子进程,这在手头的情况下意味着powershell.exe使用其-File参数进行调用。powershell.exe的启动性能成本非常重要,这会延迟执行。如果您确实想实现这种通用机制,请参阅Install-ShebangSupport.ps1底部的脚本。鉴于上述情况,这里有一种更轻量级的、特定于Python 的方法,它基于为无扩展的 shebang-line Python 脚本自动创建单独的*.ps1包装器脚本:这利用了 PowerShell 仅允许*.ps1按文件名执行其自己的脚本文件这一事实。限制:您需要至少运行一次 wrapper-script-generation 脚本(打印如下),并且每次添加新的无扩展 Python 脚本时。可以想象,文件系统观察器可用于触发生成脚本,但设置它是一项重要的工作。从好的方面来说,包装器脚本比基于文件类型的通用解决方案执行得更快,因为不涉及额外的 PowerShell 实例(子进程)。从无扩展 Python 脚本所在的目录运行以下脚本[1]:Get-ChildItem -File | Where-Object Extension -eq ''&nbsp; | % {&nbsp;&nbsp; if ((Get-Content -LiteralPath $_.fullname -First 1) -match '^#!.*\bpython') {&nbsp; &nbsp; @'py.exe ($PSCommandPath -replace '\.ps1$') $Args; exit $LASTEXITCODE'@ > ($_.FullName + '.ps1')&nbsp; }}对于每个无扩展名的 Python 脚本somescript,somescript.ps1都会创建一个伴随文件传递somescript给 Python 启动器py.exe,以及任何命令行参数;exit $LASTEXTICODE确保py.exe通过 的退出代码。正如 eryksun 指出的那样,py.exe应该能够解释 shebang 行以调用适当的 Python 可执行文件。如果您不想让包装文件弄乱您的系统,则自动生成函数作为替代方案,但请注意,您必须将它们加载到每个会话中才能使用,通常是通过您的$PROFILE文件:Get-ChildItem -File | Where-Object Extension -eq ''&nbsp; | % {&nbsp;&nbsp; if ((Get-Content -LiteralPath $_.FullName -First 1) -match '^#!.*\bpython') {&nbsp; &nbsp; Invoke-Expression @"&nbsp; &nbsp; Function global:$($_.Name) {&nbsp; &nbsp; &nbsp; py.exe "$($_.FullName)" `$Args&nbsp; &nbsp; }"@&nbsp; }}注意:这将使当前目录的无扩展名 Python 脚本可用,就好像它们位于列出的目录中一样$env:PATH- 无论当前目录是否在那里列出。每个目标 Python 脚本都被硬编码到一个同名的函数中,并且总是以该脚本为目标。相比之下,*.ps1包装器脚本文件方法允许在给定目录中进行有针对性的调用,例如.\foo.这种特殊的使用Invoke-Expression是安全的——定义基于可扩展字符串的函数——但Invoke-Expression通常应该避免。脚本Install-ShebangSupport.ps1安装在Windows扩展名的基础家当行脚本的直接执行通用支持:该脚本支持在当前用户级别(默认情况下或使用-Scope CurrentUser)或所有用户级别(使用-Scope AllUsers,需要以管理员身份运行)进行安装。假设存在于当前目录中,运行Get-Help .\Install-ShebangSupport以获得基本帮助。不带参数调用脚本会打印确认提示,其中包含有关对系统进行所需修改的详细信息;Ctrl-C可用于不安装中止;通过-Force执行安装而不提示确认。要稍后卸载,请通过-Uninstall;&nbsp;请注意,您必须匹配-Scope安装期间使用的(隐含)值。实施说明:通过cmd.exe-internal 命令定义无扩展名文件类型,assoc并且ftype总是对所有用户生效,因为定义存储在注册表中HKEY_LOCAL_MACHINE\Software\Classes;此外,调用因此总是需要提升(管理权限)。但是,它是可以创建用户级别由注册表的直接操纵,这是定义一下这个脚本使用,也为机器级的定义。注意:语法突出显示在下面的代码中被破坏,但它确实有效。<#.SYNOPSISSupport for direct execution of extension-less script files with shebang lineson Windows..DESCRIPTIONFor details, invoke this script without arguments: the confirmation promptwill show the required modifications to your system. Submit "N" to opt outof the installation.Note that extension-less files that do not have a shebang line will open in&nbsp;the default text editor..PARAMETER ScopeWhether to install support for the current user only (the default) orfor all users (requires invocation as admin)..PARAMETER UninstallUninstalls previously installed support.Note that the (implied) -Scope value must match the one that was used duringinstallation..PARAMETER ForceBypasses the confirmation prompt that is shown by default..EXAMPLEInstall-ShebangSupportInstallation for the current user that requires answering a confirmation prompt..EXAMPLEInstall-ShebangSupport -Scope AllUsers -ForceInstallation for all users without confirmation prompt. Requires invocationas admin..EXAMPLEInstall-ShebangSupport -UninstallUninstallation for the current user with confirmation prompt.#>[CmdletBinding(PositionalBinding=$false)]param(&nbsp; [ValidateSet('CurrentUser', 'AllUsers')]&nbsp; [string] $Scope = 'CurrentUser'&nbsp; ,&nbsp; [switch] $Force&nbsp; ,&nbsp; [switch] $Uninstall)$ErrorActionPreference = 'Stop'; Set-StrictMode -Version 1if ($env:OS -ne 'Windows_NT') { Throw ("This script can only run on Windows.")}# ---------------------- BEGIN: Internal helper functions# === INSTALLfunction install {&nbsp; Write-Verbose ('Installing shebang-script support for {0}:' -f ('the current user', 'ALL users')[$forAllUsers])&nbsp; # NOTE:&nbsp; #&nbsp; * assoc and ftype only ever operate on HKEY_LOCAL_MACHINE\Software\Classes, not HKEY_CURRENT_USER\Software\Classes - both on reading and writing.&nbsp; #&nbsp; * *HKEY_CURRENT_USER*-level definitions DO work, but *neither assoc nor ftype report them or can update them*.&nbsp; # Therefore, we perform direct registry manipulation below.&nbsp; Write-Verbose 'Creating file type for extension-less file names via the registry...'&nbsp; # Map the "extension-less extension", "." to the name of the new file type to be created below.&nbsp; # Caveat: Sadly, New-Item -Force blindly recreates the registry key if it already exists, discarding&nbsp; #&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;all existing content in the process.&nbsp; $key = New-Item -Force -Path $regkeyExtensionToFileType&nbsp; $null = New-ItemProperty -LiteralPath $key.PSPath&nbsp; -Name '(default)' -Value $fileTypeName&nbsp; # Define the new file type:&nbsp; $key = New-Item -Force -Path "$regkeyFileType\Shell\Open\Command"&nbsp; $null = New-ItemProperty -LiteralPath $key.PSPath&nbsp; -Name '(default)' -Value ('powershell.exe -noprofile -file "{0}" "%1" %*' -f $helperScriptFullName)&nbsp; # Get the current $env:PATHEXT definition from the registry.&nbsp; $currPathExt = [Environment]::GetEnvironmentVariable('PATHEXT', ('User', 'Machine')[$forAllUsers])&nbsp; if (-not $forAllUsers -and -not $currPathExt) {&nbsp; &nbsp; Write-Verbose "Creating a static user-level copy of the machine-level PATHEXT environment variable..."&nbsp; &nbsp; $currPathExt = [Environment]::GetEnvironmentVariable('PATHEXT', 'Machine')&nbsp; }&nbsp; # Add "." as an executable extension to $env:PATHEXT so as to support&nbsp; # direct execution of extension-less files.&nbsp; if ($currPathExt -split ';' -notcontains '.') {&nbsp; &nbsp; Write-Verbose "Appending '.' to PATHEXT..."&nbsp; &nbsp; [Environment]::SetEnvironmentVariable('PATHEXT', $currPathExt + ';.', ('User', 'Machine')[$forAllUsers])&nbsp; &nbsp; # Also define it for the running session&nbsp; &nbsp; $env:PATHEXT += ';.'&nbsp; } else {&nbsp; &nbsp; Write-Verbose "'.' is already contained in PATHEXT."&nbsp; }&nbsp; # The following here-string is the source code for the&nbsp; # $helperScriptFullName script to create.&nbsp; # To debug and/or modify it:&nbsp; #&nbsp; &nbsp;* Edit and/or debug $helperScriptFullName&nbsp; #&nbsp; &nbsp;* After applying fixes / enhancements, replace the here-string&nbsp; #&nbsp; &nbsp; &nbsp;below with the updated source code.&nbsp; @'&nbsp; # When invoked by direct execution of a script file via the file-type definition, the arguments are:&nbsp; #&nbsp; * The full path of the script file being invoked.&nbsp; #&nbsp; * Arguments passed to the script file on invocation, if any.&nbsp; #&nbsp; &nbsp; CAVEAT: PowerShell's own parsing of command-line arguments into $args&nbsp; #&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; breaks unquoted tokens such as >> -true:blue << and >> -true.blue << into *2* arguments&nbsp; #&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ('-true:', 'blue' and '-true', '.blue', respectively).&nbsp; #&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; The only way to avoid that is to pass the argument *quoted*: '-true:blue' and '-true.blue'&nbsp; #&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; See https://github.com/PowerShell/PowerShell/issues/6360&nbsp; # Parse the arguments into the script&nbsp; param(&nbsp; &nbsp; [Parameter(Mandatory=$true)] [string] $LiteralPath,&nbsp; &nbsp; [Parameter(ValueFromRemainingArguments=$true)] [array] $passThruArgs&nbsp; &nbsp;)&nbsp; $ErrorActionPreference = 'Stop'; Set-StrictMode -Version 1&nbsp; # Note: When invoked via the file-type definition, $LiteralPath is guaranteed to be a full path.&nbsp; # To also support interactive use of this script (for debugging), we resolve the script&nbsp;&nbsp; # argument to a full path.&nbsp; # Note that if you pass just a script filename (<script>), it'll be interpreted relative&nbsp;&nbsp; # to the current directory rather than based on an $env:PATH search; to do the latter,&nbsp;&nbsp; # pass (Get-Command <script>).Source&nbsp; if ($LiteralPath -notmatch '^(?:[a-z]:)?[\\/]') { $LiteralPath = Convert-Path -LiteralPath $LiteralPath }&nbsp; # Check the script's first line for a shebang.&nbsp; $shebangLine = ''&nbsp; switch -Regex -File $LiteralPath {&nbsp; &nbsp; '^#!\s*(.*)\s*$' { # Matches a shebang line.&nbsp; &nbsp; &nbsp; # Save the shebang line and its embedded command.&nbsp; &nbsp; &nbsp; $shebangLine = $_&nbsp; &nbsp; &nbsp; $cmdLine = $Matches[1]&nbsp; &nbsp; &nbsp; Write-Verbose "Shebang line found in '$LiteralPath': $shebangLine"&nbsp; &nbsp; &nbsp; break # We're done now that we have the shebang line.&nbsp; &nbsp; }&nbsp; &nbsp; default { # no shebang line found -> open with default text editor&nbsp; &nbsp; &nbsp; # Note: We cannot use Invoke-Item or Start-Process, as that would&nbsp; &nbsp; &nbsp; #&nbsp; &nbsp; &nbsp; &nbsp;reinvoke this handler, resulting in an infinite loop.&nbsp; &nbsp; &nbsp; #&nbsp; &nbsp; &nbsp; &nbsp;The best we can do is to open the file in the default text editor.&nbsp; &nbsp; &nbsp; Write-Verbose "No shebang line, opening with default text editor: $LiteralPath"&nbsp; &nbsp; &nbsp; # Obtain the command line for the default text editor directly from the registry&nbsp; &nbsp; &nbsp; # at HKEY_CLASSES_ROOT\txtfile\shell\Open\command rather than via `cmd /c ftype`,&nbsp; &nbsp; &nbsp; # because assoc and ftype only ever report on and update the *machine-level* definitions at&nbsp;&nbsp; &nbsp; &nbsp; # HKEY_LOCAL_MACHINE\Software\Classes&nbsp; &nbsp; &nbsp; $cmdLine = [environment]::ExpandEnvironmentVariables((((Get-ItemProperty -EA Ignore registry::HKEY_CLASSES_ROOT\txtfile\shell\Open\command).'(default)') -split '=')[-1])&nbsp; &nbsp; &nbsp; if (-not $cmdLine) { $cmdLine = 'NOTEPAD.EXE %1' } # Fall back to Notepad.&nbsp; &nbsp; &nbsp; break # We're done now that we know this file doesn't have a shebang line.&nbsp; &nbsp; }&nbsp; }&nbsp; # Parse the shebang line's embedded command line or the default-text-editor's command line into arguments.&nbsp; # Note: We use Invoke-Expression and Write-Output so as to support *quoted*&nbsp; #&nbsp; &nbsp; &nbsp; &nbsp;arguments as well - though presumably rare in practice.&nbsp; #&nbsp; &nbsp; &nbsp; &nbsp;If supporting quoted tokens isn't necessary, the next line can be replaced&nbsp;&nbsp; #&nbsp; &nbsp; &nbsp; &nbsp;with a strictly-by-whitespace splitting command:&nbsp; #&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$cmdArgs = -split $cmdLine&nbsp; [array] $cmdArgs = (Invoke-Expression "Write-Output -- $($cmdLine -replace '\$', "`0")") -replace "`0", '$'&nbsp; if ($shebangLine) {&nbsp; &nbsp; # Extract the target executable name or path.&nbsp; &nbsp; # If the first argument is '/usr/bin/env', we skip it, as env (on Unix-like platforms) is merely used&nbsp; &nbsp; # to locate the true target executable in the Path.&nbsp; &nbsp; $exeTokenIndex = 0 + ($cmdArgs[0] -eq '/usr/bin/env')&nbsp; &nbsp; $exeNameOrPath = $cmdArgs[$exeTokenIndex]&nbsp; &nbsp; $exeFullPath = ''&nbsp; &nbsp; # Note: We do NOT pass any remaining arguments from the shebang line through.&nbsp; &nbsp; #&nbsp; &nbsp; &nbsp; &nbsp;(Such arguments are rare anyway.)&nbsp; &nbsp; #&nbsp; &nbsp; &nbsp; &nbsp;The rationale is that an interpreter that understands shebang lines will&nbsp; &nbsp; #&nbsp; &nbsp; &nbsp; &nbsp;also respect such arguments when reading the file - this is true of at&nbsp; &nbsp; #&nbsp; &nbsp; &nbsp; &nbsp;least py.exe, the Python launcher, and ruby.exe&nbsp; &nbsp; # Python is a special case: the Python launcher, py.exe, is itself&nbsp; &nbsp; # capable of interpreting shebang lines, so we defer to it.&nbsp; &nbsp; if ($exeNameOrPath -match '\bpython\d?') {&nbsp; &nbsp; &nbsp; # Ensure that 'py.exe' is available; if not, we fall back to the same approach&nbsp;&nbsp; &nbsp; &nbsp; # as for all other executables.&nbsp; &nbsp; &nbsp; $exeFullPath = (Get-Command -CommandType Application py.exe -ErrorAction Ignore).Source&nbsp; &nbsp; }&nbsp; &nbsp; if (-not $exeFullPath) {&nbsp; &nbsp; &nbsp; # Try the executable spec. as-is first, should it actually contain a *Windows* path name.&nbsp; &nbsp; &nbsp; $exeFullPath = (Get-Command -CommandType Application $exeNameOrPath -ErrorAction Ignore).Source&nbsp; &nbsp; &nbsp; if (-not $exeFullPath) {&nbsp; &nbsp; &nbsp; &nbsp; # If not found, assume it is a Unix path that doesn't apply, and try to locate the hopefully&nbsp; &nbsp; &nbsp; &nbsp; # appropriate executable by its filename only, in the Path.&nbsp; &nbsp; &nbsp; &nbsp; $exeFullPath = (Get-Command -CommandType Application (Split-Path -Leaf -LiteralPath $exeNameOrPath) -ErrorAction Ignore).Source&nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; # Abort, if we can't find a suitable executable.&nbsp; &nbsp; if (-not $exeFullPath) { Throw "Could not find a suitable executable to run '$LiteralPath' based on its shebang line: $shebangLine" }&nbsp; &nbsp; # Synthesize the complete list of arguments to pass to the target exectuable.&nbsp; &nbsp; $passThruArgs = , $LiteralPath + $passThruArgs&nbsp; } else {&nbsp; # NON-shebang-line files: invocation of default text editor&nbsp; &nbsp; $exeFullPath, [array] $editorArgs = $cmdArgs -replace '%1', ($LiteralPath -replace '\$', '$$')&nbsp; &nbsp; # Synthesize the complete list of arguments to pass to the target exectuable.&nbsp; &nbsp; # Replace the '%1' placeholder with the script's path.&nbsp; &nbsp; # Note that we don't really expect additional arguments to have been passed in this scenario,&nbsp; &nbsp; # and such arguments may be interpreted as additional file arguments by the editor.&nbsp; &nbsp; $passThruArgs = ($editorArgs -replace '"?%1"?', ($LiteralPath -replace '\$', '$$$$')) + $passThruArgs&nbsp; &nbsp; # If the editor is a GUI application, $LASTEXITCODE won't be set by PowerShell.&nbsp; &nbsp; # We set it to 0 here, as it has no value by default, and referencing it below with exit&nbsp; &nbsp; # would cause an error due to Set-StrictMode -Version 1.&nbsp; &nbsp; $LASTEXITCODE = 0&nbsp; }&nbsp; Write-Verbose "Executing: $exeFullPath $passThruArgs"&nbsp; # Invoke the target executable with all arguments.&nbsp; # Important:&nbsp; #&nbsp; * We need to manually \-escape embeded " chars. in arguments&nbsp; #&nbsp; &nbsp; because PowerShell, regrettably, doesn't do that automatically.&nbsp; #&nbsp; &nbsp; However, even that may fail in edge cases in Windows PowerShell (fixed in PS Core),&nbsp;&nbsp; #&nbsp; &nbsp; namely when an unbalanced " char. is part of the first word - see https://stackoverflow.com/a/55604316/45375&nbsp; & $exeFullPath ($passThruArgs&nbsp; -replace '"', '\"')&nbsp; # Pass the target executable's exit code through.&nbsp; # (In the case of invoking the default editor for non-shebang-line files, it&nbsp;&nbsp; # won't have been set, if the editor is a GUI application.)&nbsp; exit $LASTEXITCODE'@ |&nbsp; &nbsp; Set-Content -Encoding Utf8 -LiteralPath $helperScriptFullName}# === UNINSTALLfunction uninstall {&nbsp; Write-Verbose ('Uninstalling shebang-script support for {0}:' -f ('the current user', 'ALL users')[$forAllUsers])&nbsp; Write-Verbose 'Removing file type information from the registry...'&nbsp; foreach ($regKey in $regkeyExtensionToFileType, $regkeyFileType) {&nbsp; &nbsp; if (Test-Path -LiteralPath $regKey) {&nbsp; &nbsp; &nbsp; Remove-Item -Force -Recurse -LiteralPath $regkey&nbsp; &nbsp; }&nbsp; }&nbsp; # Get the current $env:PATHEXT definition from the registry.&nbsp; $currPathExt = [Environment]::GetEnvironmentVariable('PATHEXT', ('User', 'Machine')[$forAllUsers])&nbsp; # Remove the "." entry from $env:PATHEXT&nbsp; $newPathExt = ($currPathExt -split ';' -ne '.') -join ';'&nbsp; if ($newPathExt -eq $currPathExt) {&nbsp; &nbsp; Write-Verbose "'.' is not contained in PATHEXT; nothing to do."&nbsp; } else {&nbsp; &nbsp; # For user-level uninstallations: as a courtesy, we compare the new PATHEXT value&nbsp; &nbsp; # to the machine-level one, and, if they're now the same, simply REMOVE the user-level definition.&nbsp; &nbsp; Write-Verbose "Removing '.' from PATHEXT..."&nbsp; &nbsp; if (-not $forAllUsers) {&nbsp; &nbsp; &nbsp; $machineLevelPathExt = [Environment]::GetEnvironmentVariable('PATHEXT', 'Machine')&nbsp; &nbsp; &nbsp; if ($newPathExt -eq $machineLevelPathExt) { $newPathExt = $null }&nbsp; &nbsp; &nbsp; Write-Verbose "User-level PATHEXT no longer needed, removing..."&nbsp; &nbsp; }&nbsp; &nbsp; [Environment]::SetEnvironmentVariable('PATHEXT', $newPathExt, ('User', 'Machine')[$forAllUsers])&nbsp; &nbsp; # Also update for the running session&nbsp; &nbsp; $env:PATHEXT = if ($newPathExt) { $newPathExt } else { $machineLevelPathExt }&nbsp; }&nbsp; Write-Verbose "Removing helper PowerShell script..."&nbsp; if (Test-Path -LiteralPath $helperScriptFullName) {&nbsp; &nbsp; Remove-Item -Force -LiteralPath $helperScriptFullName&nbsp; }}# ---------------------- END: Internal helper functions$forAllUsers = $Scope -eq 'AllUsers'$verb = ('install', 'uninstall')[$Uninstall.IsPresent]$operation = $verb + 'ation'# If -Scope AllUsers was passed, ensure that the session is elevated.$mustElevate = $forAllUsers -and -not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole('BUILTIN\Administrators')if ($mustElevate) {&nbsp; Throw "In order to $verb for ALL users, you must run this script WITH ELEVATED PRIVILEGES (Run As Administrator)."}# --- Define names, registry and file locations.# The path of the generic shebang runner script that we'll create below.$helperScriptFullName = Join-Path ($HOME, $env:ALLUSERSPROFILE)[$forAllUsers] 'Invoke-ShebangScript.ps1'# The name of the file type to create for extension-less files.$fileTypeName = 'ShebangScript'# Registry keys that need to be modified.# "." represents extension-less files$regkeyExtensionToFileType = 'registry::{0}\SOFTWARE\Classes\.' -f ('HKEY_CURRENT_USER', 'HKEY_LOCAL_MACHINE')[$forAllUsers]$regkeyFileType = 'registry::{0}\SOFTWARE\Classes\{1}' -f ('HKEY_CURRENT_USER', 'HKEY_LOCAL_MACHINE')[$forAllUsers], $fileTypeName# ---&nbsp;# Prompt for confirmation, unless -Force was passsed.if ($Uninstall) { # UNINSTALL&nbsp; if (-not $Force -and -not $PSCmdlet.ShouldContinue(@"You are about to UNINSTALL support for direct execution of extension-lessscript files that have shebang lines.Uninstallation will be performed for $(("the CURRENT USER only`n(invoke as admin with -Scope AllUsers to change that)", 'ALL USERS')[$forAllUsers]).IMPORTANT: Uninstallation will only be effective if it is performed in the same&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(implied) -Scope as the original installation.The following modifications to your system will be performed:&nbsp; * "." will be persistently REMOVED from your `$env:PATHEXT variable.&nbsp; * The following registry keys will be REMOVED:&nbsp; &nbsp; &nbsp; $($regkeyExtensionToFileType -replace '^registry::')&nbsp; &nbsp; &nbsp; $($regkeyFileType -replace '^registry::')&nbsp; * The following helper PowerShell script will be REMOVED:&nbsp; &nbsp; $helperScriptFullName&nbsp;Press ENTER to proceed, or Ctrl-C to abort."@, "Shebang-Script Direct-Execution Support - Uninstallation")) { # , $true, [ref] $null, [ref] $null)) {&nbsp; &nbsp; exit 1&nbsp; }&nbsp; # Call the uninstallation helper function&nbsp; uninstall} else {&nbsp; # INSTALL&nbsp; if (-not $Force -and -not $PSCmdlet.ShouldContinue(@"You are about to install support for direct execution of Unix-style scripts&nbsp;that do not have a filename extension and instead define the interpreter to runthem with via shebangline ("#!/path/to/interpreter").Support will be installed for $(("the CURRENT USER only`n(invoke as admin with -Scope AllUsers to change that)", 'ALL USERS')[$forAllUsers]).Once installed, you will be able to run such scripts by direct invocation,via a helper PowerShell script that analyzes the shebang line and calls theappropriate interpreter.CAVEATS:&nbsp; * ENABLING THIS INVOCATION MECHANISM IS A SECURITY RISK, because any&nbsp; &nbsp; plain-text file without an extension that has a shebang line&nbsp; &nbsp; effectively becomes executable, potentially bypassing security features&nbsp; &nbsp; that focus on files that have extensions known to be executable.&nbsp; * AS OF POWERSHELL CORE 6.2.0, direct execution of such extension-less files&nbsp; &nbsp; from PowerShell INVARIABLY RUNS IN A NEW CONSOLE WINDOW, WHICH MAKES USE&nbsp; &nbsp; FROM POWERSHELL VIRTUALLY USELESS.&nbsp; &nbsp; However, this is a BUG that should be fixed; see:&nbsp; &nbsp; &nbsp; https://github.com/PowerShell/PowerShell/issues/7769The following modifications to your system will be performed:&nbsp; * "." will be added persistently to your `$env:PATHEXT variable, to enable&nbsp; &nbsp; direct execution of filenames without extension.&nbsp; &nbsp; NOTE: If you install with -Scope CurrentUser (the default), a static&nbsp;&nbsp; &nbsp; user-level copy of the machine-level PATHEXT environment variable is&nbsp;&nbsp; &nbsp; created, unless already present.&nbsp; * The following registry locations will be created or replaced to define a&nbsp; &nbsp; new file type for extension-less filenames:&nbsp; &nbsp; &nbsp; $($regkeyExtensionToFileType -replace '^registry::')&nbsp; &nbsp; &nbsp; $($regkeyFileType -replace '^registry::')&nbsp; * The helper PowerShell script will be created in:&nbsp; &nbsp; $helperScriptFullName&nbsp;NOTE: Any existing registry definitions or helper script will be REPLACED.Press ENTER to proceed, or CTRL-C to abort."@, "Shebang-Script Direct-Execution Support - Installation")) {&nbsp; &nbsp; # !! The prompt defaults to *Yes* (!)&nbsp; &nbsp; # !! Sadly, if we wanted the prompt to be default to *No*, we'de be forced&nbsp;&nbsp; &nbsp; # !! to also present pointless 'Yes/No to *All*' prompts, which would be confusing.&nbsp; &nbsp; # !! See https://github.com/PowerShell/PowerShell/issues/9428&nbsp; &nbsp; exit 1&nbsp; }&nbsp; # Call the installation helper function&nbsp; install}Write-Verbose "Shebang-support ${operation} completed."if (-not $Force) {&nbsp; Write-Host "Shebang-support ${operation} completed."}exit 0
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Python