使用PowerShell以管理员身份运行命令?

425

你知道如果您是系统的管理员用户,您可以右键点击批处理脚本并作为管理员运行而无需输入管理员密码吗?

我想知道如何在PowerShell脚本中实现这一点。 我不想输入密码; 我只想模拟右键单击“以管理员身份运行”的方法。

到目前为止,我阅读的所有内容都需要提供管理员密码。


5
试试gsudo。它是Windows的一个免费开源sudo工具,允许从命令行以管理员身份执行操作。会出现UAC弹窗。 - Gerardo Grignoli
28个回答

455

如果当前控制台没有提升权限,而你尝试的操作需要提升权限,则可以使用“以管理员身份运行”选项启动powershell

PS> Start-Process powershell -Verb runAs

Microsoft Docs: Start-Process


4
CMD窗口内部来看,最短的版本是:powershell start cmd -v runas。要验证新获取的特权,请运行:net sess - 303
4
注:仅在输入时使用简短版本。在脚本中,出于可读性的原因,请使用完整长度。 - Redwolf
2
使用Start-Process wt -Verb runAs以管理员身份启动Windows终端。 - Phillip Schulze
1
@ᄂᄀ 不,这就是如何启动提升的PowerShell控制台。请看OP问题中的这一行:“我只是想模仿右键以管理员身份运行的方法”。 - TylerH
1
@ᄂᄀ 这篇帖子的正文明确多次提到,这个问题是关于右键点击文件并“以管理员身份运行”的。如果你知道如何通过其他途径达到相同的结果,那当然很好,但请不要误导他人,暗示这个问题并不是在询问它所问的内容。 - TylerH
显示剩余7条评论

166

这里是对Shay Levi建议的补充(只需在脚本开头添加这些行):

if (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))  
{  
  $arguments = "& '" +$myinvocation.mycommand.definition + "'"
  Start-Process powershell -Verb runAs -ArgumentList $arguments
  Break
}

这将导致当前的脚本被传递到一个新的以管理员身份运行的PowerShell进程中(如果当前用户有访问管理员权限的能力,并且该脚本没有以管理员身份运行)。


可以轻松修改为仅在非管理员权限下运行时抛出错误。只需将if语句放入then块中并添加一个throw即可。 - jpmc26
有没有一种方法可以在以管理员身份运行PowerShell时抑制Windows的提示?对于需要静默运行此类应用程序的情况,用户被询问可能会很烦人。 - Pelicer
这在函数定义内部会失败。必须放在外面,否则它会打开一个新的管理员窗口并立即关闭。 - Jim
1
如果脚本从映射的网络驱动器启动,管理员没有相同的映射驱动器,则无法正常工作。要修复此问题,您必须从UNC地址运行或具有将网络驱动器扩展到UNC然后从UNC重新启动的代码。 - gregg
非常好的解决方案,非常适合我的一般应用场景。 - Lancer.Yan
显示剩余3条评论

142
自升级的PowerShell脚本
Windows 8.1 / PowerShell 4.0+
if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { Start-Process powershell.exe "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs; exit }

# Your script here

234
严格来说,如果以这种格式排版,一切都是“一行”,但这并不意味着它实际上是“一行”。 - illegal-immigrant
3
缺点:如果在提示符中输入非管理员账户,你会陷入无限的分支-退出循环。 - Manuel Faux
4
但这不传递参数。 - kyb
5
为了传递参数,我对它进行了修改:if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { Start-Process powershell.exe "-NoProfile -ExecutionPolicy Bypass -File \"$PSCommandPath`" `"$args`"" -Verb RunAs; exit }`。 - ab.
4
这个答案没有保留工作目录。请参考这里的一个可以保留工作目录的答案:stackoverflow.com/a/57035712/2441655。 - Venryx
显示剩余10条评论

58

Benjamin Armstrong发布了一篇关于自我提升的PowerShell脚本的优秀文章。他的代码有一些小问题;以下是基于评论中建议的修复的修改版本。

基本上,它获取与当前进程相关联的标识符,检查它是否为管理员,如果不是,则创建一个具有管理员特权的新PowerShell进程并终止旧进程。

# Get the ID and security principal of the current user account
$myWindowsID = [System.Security.Principal.WindowsIdentity]::GetCurrent();
$myWindowsPrincipal = New-Object System.Security.Principal.WindowsPrincipal($myWindowsID);

# Get the security principal for the administrator role
$adminRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator;

# Check to see if we are currently running as an administrator
if ($myWindowsPrincipal.IsInRole($adminRole))
{
    # We are running as an administrator, so change the title and background colour to indicate this
    $Host.UI.RawUI.WindowTitle = $myInvocation.MyCommand.Definition + "(Elevated)";
    $Host.UI.RawUI.BackgroundColor = "DarkBlue";
    Clear-Host;
}
else {
    # We are not running as an administrator, so relaunch as administrator

    # Create a new process object that starts PowerShell
    $newProcess = New-Object System.Diagnostics.ProcessStartInfo "PowerShell";

    # Specify the current script path and name as a parameter with added scope and support for scripts with spaces in it's path
    $newProcess.Arguments = "& '" + $script:MyInvocation.MyCommand.Path + "'"

    # Indicate that the process should be elevated
    $newProcess.Verb = "runas";

    # Start the new process
    [System.Diagnostics.Process]::Start($newProcess);

    # Exit from the current, unelevated, process
    Exit;
}

# Run your code that needs to be elevated here...

Write-Host -NoNewLine "Press any key to continue...";
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown");

我无法正确解析脚本名称和参数,因此我将执行过程包装在cmd.exe /c中。 $newProcess = new-object System.Diagnostics.ProcessStartInfo “cmd.exe” $newProcess.Arguments = ‘/c ‘ + [System.Environment]::GetCommandLineArgs() $newProcess.WorkingDirectory = [environment]::CurrentDirectory - xverges
这样做有什么优势,而不是使用Start-Process吗?我对此方法与上面和其他线程中发布的其他方法之间的差异很感兴趣。它们都依赖于.NET,但这种方法更加重视... - ZaxLofful
我发现与Armstrong帖子的直接链接相关的各种评论也非常有帮助。 - BentChainRing
3
这个答案没有保留工作目录。参见这里提供一个能够保留工作目录的答案:stackoverflow.com/a/57035712/2441655 - Venryx

53

这是一个用于 Powershell 脚本的自升级代码片段,它可以保留工作目录:

if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
    Start-Process PowerShell -Verb RunAs "-NoProfile -ExecutionPolicy Bypass -Command `"cd '$pwd'; & '$PSCommandPath';`"";
    exit;
}

# Your script here

对于执行路径相对操作的脚本而言,保留工作目录非常重要。几乎所有其他答案都没有保留此路径,这可能会导致脚本的其余部分出现意外错误。

如果您不想使用自我提升的脚本/代码段,而只是想以管理员身份轻松启动脚本(例如,从资源管理器上下文菜单中),请参阅我的其他回答:https://dev59.com/aKLia4cB1Zd3GeqPfTNj#57033941


34

您可以创建一个批处理文件(*.bat),通过双击运行它来以管理员特权运行您的PowerShell脚本。这样,您不需要更改您的PowerShell脚本中的任何内容。要实现此功能,请创建一个与您的PowerShell脚本具有相同名称和位置的批处理文件,然后将以下内容放入其中:

@echo off

set scriptFileName=%~n0
set scriptFolderPath=%~dp0
set powershellScriptFileName=%scriptFileName%.ps1

powershell -Command "Start-Process powershell \"-ExecutionPolicy Bypass -NoProfile -NoExit -Command `\"cd \`\"%scriptFolderPath%`\"; & \`\".\%powershellScriptFileName%\`\"`\"\" -Verb RunAs"

就是这样!

以下是解释:

假设你的PowerShell脚本位于路径C:\Temp\ScriptTest.ps1,则批处理文件必须具有路径C:\Temp\ScriptTest.bat。 当有人执行此批处理文件时,将发生以下步骤:

  1. 命令提示符将执行该命令:

powershell -Command "Start-Process powershell \"-ExecutionPolicy Bypass -NoProfile -NoExit -Command `\"cd \`\"C:\Temp\`\"; & \`\".\ScriptTest.ps1\`\"`\"\" -Verb RunAs"
一个新的 PowerShell 会话将被打开并执行以下命令:
Start-Process powershell "-ExecutionPolicy Bypass -NoProfile -NoExit -Command `"cd \`"C:\Temp\`"; & \`".\ScriptTest.ps1\`"`"" -Verb RunAs
  • 将以管理员权限在system32文件夹中打开另一个新的powershell会话,并传递以下参数:

  • -ExecutionPolicy Bypass -NoProfile -NoExit -Command "cd \"C:\Temp\"; & \".\ScriptTest.ps1\""
    
    下面的命令将以管理员权限执行:
    cd "C:\Temp"; & ".\ScriptTest.ps1"
    

    一旦脚本路径和名称参数用双引号括起来,它们可以包含空格或单引号字符(')。

  • 当前文件夹将从system32更改为C:\Temp,并且将执行脚本ScriptTest.ps1。 一旦传递了参数-NoExit,即使您的powershell脚本引发了异常,窗口也不会关闭。


  • 4
    如果您需要自动化它,那么确保自动化过程以管理员身份运行是您的责任。如果您可以自动将非管理员进程提升为管理员进程而无需用户交互,则这将打败首先需要进程具有管理员特权的目的。 - meustrus
    1
    这个答案比其他答案更好,因为它保留了当前工作目录。我在这里组合了一些单行变体的方法(一个是用于cmd/batch,一个是用于Explorer上下文菜单项,还有一个是用于powershell):https://dev59.com/aKLia4cB1Zd3GeqPfTNj#57033941 - Venryx
    2
    就我个人而言,@mems的第5次编辑对我来说是错误的。在%scriptFolderPath%之后添加反斜杠会导致错误。 - topshot
    使用批处理文件来运行PowerShell脚本似乎有点笨拙,因为PowerShell脚本具有比批处理文件更强大和灵活的10倍功能...这只是我的个人看法。 - Jim
    1
    @tgonzalez89,反引号将双引号字符与反斜杠字符隔离开来,以便双引号字符不会过早地被取消转义。 - Dan Henderson
    显示剩余4条评论

    26

    使用#Requires -RunAsAdministrator命令是自PowerShell 4.0版本才开始提供的。此命令用于指定在运行脚本时,必须以管理员权限启动Windows PowerShell会话。详情请参阅http://technet.microsoft.com/en-us/library/hh847765.aspx

    尽管这似乎是解决问题的好方法,但我还不确定是否有实际应用经验。如果脚本在非管理员模式下运行,则会出现以下错误:

    The script 'StackOverflow.ps1' cannot be run because it contains a "#requires" statement for running as Administrator. The current Windows PowerShell session is not running as Administrator. Start Windows PowerShell by using the Run as Administrator option, and then try running the script again.

    另外,PowerShell 3.0运行时可能会忽略此命令,甚至会导致错误。

    + CategoryInfo          : PermissionDenied: (StackOverflow.ps1:String) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : ScriptRequiresElevation
    

    12
    很遗憾,这只会使脚本在Shell没有管理员权限时失败并显示错误信息,它本身不会提升权限。 - Jacek Gorgoń
    1
    PS3似乎出现了一个错误,如建议所示。我得到了“参数RunAsAdministrator需要一个参数”的提示。@akauppi我并不认为他们总是在思考。 - jpmc26

    16

    您可以轻松地添加一些注册表条目,以获得.ps1文件的“以管理员身份运行”上下文菜单:

    您可以通过编辑注册表来为.ps1文件添加“以管理员身份运行”上下文菜单。

    New-Item -Path "Registry::HKEY_CLASSES_ROOT\Microsoft.PowershellScript.1\Shell\runas\command" `
    -Force -Name '' -Value '"c:\windows\system32\windowspowershell\v1.0\powershell.exe" -noexit "%1"'
    

    (从@Shay那里更新了一个更简单的脚本)

    基本上,在 HKCR:\Microsoft.PowershellScript.1\Shell\runas\command 处将默认值设置为使用 Powershell 调用脚本。


    它不起作用,你正在创建一个“(default)”键,而不是更新“(default)”键值。我已经将代码压缩成了适合我的一行代码。你能测试一下吗?New-Item -Path "Registry::HKEY_CLASSES_ROOT\Microsoft.PowershellScript.1\Shell\runas\command" -Force -Name '' -Value '"c:\windows\system32\windowspowershell\v1.0\powershell.exe" -noexit "%1"' - Shay Levy
    @Shay Levy - 嗨Shay,感谢您的更新。我已经用它更新了答案。它确实可行。但是我之前的那个也可以工作,尽管它比较冗长。我之前没有用PowerShell进行过太多的reg编辑,但使用“(default)”进行编辑是我看到的一个例子。它没有创建新的键(像"default"一样),但确实按预期更新了默认键。您是否尝试过它还是只是从“(default)”部分猜测的呢? - manojlds
    我尝试了一下。它在命令键下创建了一个“(默认)”键。 - Shay Levy
    这对我有用,经过对注册表值的一些小改动:"c:\windows\system32\windowspowershell\v1.0\powershell.exe" -ExecutionPolicy RemoteSigned -NoExit "& '%1'" - MikeBaz - MSFT
    1
    答案中的注册表值存在各种问题。它实际上并没有运行命令,并且没有正确引用脚本名称(这意味着在路径中带有空格时会出错)。我正在成功使用以下内容:"c:\windows\system32\windowspowershell\v1.0\powershell.exe" -noexit -command "& '%1'" - Kal Zekdor
    一种替代上下文菜单命令,不使用“runas”键,并保留当前目录(请参见答案的第2节):https://dev59.com/aKLia4cB1Zd3GeqPfTNj#57033941 - Venryx

    14

    Jonathan和Shay Levy发布的代码对我无效。

    请找到下面的有效代码:

    If (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
    {   
    #"No Administrative rights, it will display a popup window asking user for Admin rights"
    
    $arguments = "& '" + $myinvocation.mycommand.definition + "'"
    Start-Process "$psHome\powershell.exe" -Verb runAs -ArgumentList $arguments
    
    break
    }
    #"After user clicked Yes on the popup, your file will be reopened with Admin rights"
    #"Put your code here"
    

    2
    非常有用和实用的解决方案:只需将其添加到我的脚本中,它就可以工作了。 - CDuv
    3
    你能否指出容易被忽略的差异,以使你的读者受益?说实话,这种修改不值得比在其他答案上发表评论更多的关注。 - jpmc26

    11

    请以管理员权限重新运行脚本,并检查脚本是否在此模式下启动。下面我编写了一个包含两个函数的脚本:DoElevatedOperationsDoStandardOperations。您应该将需要管理员权限的代码放入第一个函数中,将标准操作放入第二个函数中。变量IsRunAsAdmin用于识别管理员模式。

    我的代码是从创建Windows Store应用程序包时自动生成的Microsoft脚本中提取的简化版本。

    param(
        [switch]$IsRunAsAdmin = $false
    )
    
    # Get our script path
    $ScriptPath = (Get-Variable MyInvocation).Value.MyCommand.Path
    
    #
    # Launches an elevated process running the current script to perform tasks
    # that require administrative privileges.  This function waits until the
    # elevated process terminates.
    #
    function LaunchElevated
    {
        # Set up command line arguments to the elevated process
        $RelaunchArgs = '-ExecutionPolicy Unrestricted -file "' + $ScriptPath + '" -IsRunAsAdmin'
    
        # Launch the process and wait for it to finish
        try
        {
            $AdminProcess = Start-Process "$PsHome\PowerShell.exe" -Verb RunAs -ArgumentList $RelaunchArgs -PassThru
        }
        catch
        {
            $Error[0] # Dump details about the last error
            exit 1
        }
    
        # Wait until the elevated process terminates
        while (!($AdminProcess.HasExited))
        {
            Start-Sleep -Seconds 2
        }
    }
    
    function DoElevatedOperations
    {
        Write-Host "Do elevated operations"
    }
    
    function DoStandardOperations
    {
        Write-Host "Do standard operations"
    
        LaunchElevated
    }
    
    
    #
    # Main script entry point
    #
    
    if ($IsRunAsAdmin)
    {
        DoElevatedOperations
    }
    else
    {
        DoStandardOperations
    }
    

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接