首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >通过start-process将来自powershell脚本的stdout、stderr重定向为admin

通过start-process将来自powershell脚本的stdout、stderr重定向为admin
EN

Stack Overflow用户
提问于 2018-06-09 01:43:59
回答 1查看 4.4K关注 0票数 5

在powershell脚本中,我运行了一个命令,该命令以管理员身份启动一个新的powershell (如果不是这样的话,如果需要,取决于$arg),然后运行该脚本。

我正在尝试将stdout和stderr重定向到第一个终端。

不是为了让事情变得更容易,也有争论。

代码语言:javascript
复制
param([string]$arg="help")

if($arg -eq "start" -Or $arg -eq "stop")
{
    if(![bool](([System.Security.Principal.WindowsIdentity]::GetCurrent()).groups -match "S-1-5-32-544"))
    {
        Start-Process powershell -Verb runas -ArgumentList " -file servicemssql.ps1 $arg"
        exit
    }
}

$Services = "MSSQLSERVER", "SQLSERVERAGENT", "MSSQLServerOLAPService", "SSASTELEMETRY", "SQLBrowser", `
"SQLTELEMETRY", "MSSQLLaunchpad", "SQLWriter", "MSSQLFDLauncher"

function startsql {
    "starting SQL services"
    Foreach ($s in $Services) {
        "starting $s"
        Start-Service -Name "$s"
    }
}

function stopsql {
    "stopping SQL services"
    Foreach ($s in $Services) {
        "stopping $s"
        Stop-Service -Force -Name "$s"
    }
}

function statussql {
    "getting SQL services status"
    Foreach ($s in $Services) {
        Get-Service -Name "$s"
    }
}

function help {
    "usage: StartMssql [status|start|stop]"
}

Switch ($arg) {
    "start" { startsql }
    "stop" { stopsql }
    "status" { statussql }
    "help" { help }
    "h" { help }
}

在SO上使用以下答案不起作用:

如何处理双引号内的双引号,同时保留变量($arg)扩展?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-06-09 11:53:52

PowerShell的Start-Process命令:

  • 确实有-RedirectStandardOut-RedirectStandardError参数,
  • ,但在语法上它们不能与-Verb Runas组合,这是启动进程所需的参数提升(具有管理权限)。

此约束也反映在基础.NET应用编程接口中,将System.Diagnostics.ProcessStartInfo实例上的.UseShellExecute属性设置为true -能够使用.Verb = "RunAs"以便运行提升的先决条件-意味着您不能使用.RedirectStandardOutput.RedirectStandardError属性。

总体而言,这表明您不能直接从非提升进程捕获提升进程的输出流。

纯PowerShell解决方案并非易事:

代码语言:javascript
复制
param([string] $arg='help')

if ($arg -in 'start', 'stop') {
  if (-not (([System.Security.Principal.WindowsPrincipal] [System.Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole('Administrators'))) {

    # Invoke the script via -Command rather than -File, so that 
    # a redirection can be specified.
    $passThruArgs = '-command', '&', 'servicemssql.ps1', $arg, '*>', "`"$PSScriptRoot\out.txt`""

    Start-Process powershell -Wait -Verb RunAs -ArgumentList $passThruArgs

    # Retrieve the captured output streams here:
    Get-Content "$PSScriptRoot\out.txt"

    exit
  }
}

# ...

  • 而不是-File-Command用于调用脚本,因为这允许将重定向附加到命令:*>重定向所有输出流。

代码语言:javascript
复制
- @soleil suggests using `Tee-Object` as an alternative so that the output produced by the elevated process is not only captured, but also printed to the (invariably new window's) console as it is being produced:

..., $arg, '|', 'Tee-Object', '-FilePath', "`"$PSScriptRoot\out.txt`""

代码语言:javascript
复制
- Caveat: While it doesn't make a difference in this simple case, it's important to know that arguments are parsed differently between `-File` and `-Command` modes; in a nutshell, with `-File`, the arguments following the script name are treated as _literals_, whereas the arguments following `-Command` form a command that is evaluated according to normal PowerShell rules in the target session, which has implications for escaping, for instance; notably, values with embedded spaces must be surrounded with quotes as part of the value.

  • 输出捕获文件$PSScriptRoot\out.txt中的$PSScriptRoot\路径组件可确保在调用脚本所在的同一文件夹中创建该文件(提升的进程默认使用$env:SystemRoot\System32作为工作目录)

代码语言:javascript
复制
- Similarly, this means that script file `servicemssql.ps1`, if it is invoked without a path component, must be in one of the directories listed in `$env:PATH` in order for the elevated PowerShell instance to find it; otherwise, a full path is also required, such as `$PSScriptRoot\servicemssql.ps1`.

  • -Wait确保在提升的进程退出之前不会返回控制,此时可以检查文件$PSScriptRoot\out.txt

至於跟进问题:

更进一步,我们是否有办法让管理外壳运行时不可见,并从非-privileged外壳中读取文件,就像我们使用Unix中的tail -f一样?

可以在不可见的情况下运行提升的进程本身,但请注意,您仍然会得到UAC确认提示。(如果您要关闭UAC (不推荐),则可以使用Start-Process -NoNewWindow在同一窗口中运行该进程。)

要同时监控正在产生的输出,只使用PowerShell的解决方案 tail -f**-style**,既不是微不足道的,也不是最有效的;换句话说:

代码语言:javascript
复制
param([string]$arg='help')

if ($arg -in 'start', 'stop') {
  if (-not (([System.Security.Principal.WindowsPrincipal] [System.Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole('Administrators'))) {

    # Delete any old capture file.
    $captureFile = "$PSScriptRoot\out.txt"
    Remove-Item -ErrorAction Ignore $captureFile

    # Start the elevated process *hidden and asynchronously*, passing
    # a [System.Diagnostics.Process] instance representing the new process out, which can be used
    # to monitor the process
    $passThruArgs = '-noprofile', '-command', '&',  "servicemssql.ps1", $arg, '*>', $captureFile
    $ps = Start-Process powershell -WindowStyle Hidden -PassThru  -Verb RunAs -ArgumentList $passThruArgs

    # Wait for the capture file to appear, so we can start
    # "tailing" it.
    While (-not $ps.HasExited -and -not (Test-Path -LiteralPath $captureFile)) {
      Start-Sleep -Milliseconds 100  
    }

    # Start an aux. background that removes the capture file when the elevated
    # process exits. This will make Get-Content -Wait below stop waiting.
    $jb = Start-Job { 
      # Wait for the process to exit.
      # Note: $using:ps cannot be used directly, because, due to
      #       serialization/deserialization, it is not a live object.
      $ps = (Get-Process -Id $using:ps.Id)
      while (-not $ps.HasExited) { Start-Sleep -Milliseconds 100 }
      # Get-Content -Wait only checks once every second, so we must make
      # sure that it has seen the latest content before we delete the file.
      Start-Sleep -Milliseconds 1100 
      # Delete the file, which will make Get-Content -Wait exit (with an error).
      Remove-Item -LiteralPath $using:captureFile 
    }

    # Output the content of $captureFile and wait for new content to appear
    # (-Wait), similar to tail -f.
    # `-OutVariable capturedLines` collects all output in
    # variable $capturedLines for later inspection.
    Get-Content -ErrorAction SilentlyContinue -Wait -OutVariable capturedLines -LiteralPath $captureFile

    Remove-Job -Force $jb  # Remove the aux. job

    Write-Verbose -Verbose "$($capturedLines.Count) line(s) captured."

    exit
  }
}

# ...
票数 10
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50765949

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档