【问题标题】:Start a detached background process in PowerShell在 PowerShell 中启动分离的后台进程
【发布时间】:2014-09-21 07:20:44
【问题描述】:

我有一个 Java 程序,我想从 PowerShell 脚本作为后台进程启动它,类似于守护程序在 Linux 上运行的方式。 PowerShell 脚本需要做几件事:

  1. 在后台将程序作为单独的分离进程运行,这意味着可以关闭父窗口并继续运行。
  2. 将程序的标准输出和标准错误重定向到文件。
  3. 将后台进程的 PID 保存到文件中,以便稍后可以由另一个脚本终止。

我在 Linux 上有一个 shell 脚本,它可以像这样启动程序:

$ java -jar MyProgram.jar >console.out 2>console.err &

我希望使用 PowerShell 脚本在 Windows 上复制相同的行为。我曾尝试将Start-Process 与各种选项组合一起使用,以及创建System.Diagnostics.ProcessStartInfoSystem.Diagnostics.Process 对象,但到目前为止我没有任何运气。 PowerShell 将程序作为后台进程启动,但当启动 PowerShell 会话的 DOS 窗口关闭时,程序会突然终止。我希望它在后台启动并且独立于启动它的命令窗口。

输出重定向也很麻烦,因为似乎只能在同一窗口中运行的进程中重定向输出和错误流(例如,使用-NoNewWindow)。

这种事情在 PowerShell 中是否可行?

【问题讨论】:

  • 您需要有 Powershell v3 或更高版本,并使用Start-PSSession cmdlet
  • 由于您使用的是 Powershell 2,因此这是 stackoverflow.com/questions/185575/… 的副本
  • 我在以前的谷歌搜索中看到了该帖子,但我认为该问题的 OP 并不是要关闭原始命令窗口并仍然保持后台作业运行。当您在 Linux 上执行此操作时,“孤立的”后台进程只是成为系统进程的子进程。不过,Windows 似乎不喜欢这种东西。它是否有任何机制在父进程关闭后保持进程运行?
  • stackoverflow.com/questions/23091731/… ... 使用 WMI 创建分离的进程。总帐
  • 您不需要 Powershell 来执行此操作,只需使用名为 Start 的 CMD.EXE(DOS 会话多年来不再意味着任何东西)命令。一旦你这样做了,结果将是一个分离的进程,但不是一个守护进程,如果你关闭你的交互式会话,分离的进程将会死掉。真正等同于守护进程的是 Wndows OS 上的服务。

标签: windows powershell


【解决方案1】:

为此使用jobs

Start-Job -ScriptBlock {
  & java -jar MyProgram.jar >console.out 2>console.err
}

另一个选项是Start-Process:

Start-Process java -ArgumentList '-jar', 'MyProgram.jar' `
  -RedirectStandardOutput '.\console.out' -RedirectStandardError '.\console.err'

【讨论】:

  • 谢谢,@Ansgar。我对文档的理解是 Start-Job 仅适用于启动其他 PowerShell 脚本,但这可能是错误的,我会尝试您的建议。 Start-Process 不起作用,因为它无法在新窗口中启动进程,也无法在同一调用中重定向输出流,似乎是其中之一。
  • @Dave Start-Job 可以运行脚本块,其中可能包含任何类型的 PowerShell 代码,包括对外部命令的调用。至于Start-Process:当我使用运行System.out.println("foo"); System.err.println("bar"); 的简单应用程序对其进行测试时,它工作得很好(在我修复了-RedirectStdardError 中的错字之后)。
  • 它是否以分离的进程启动,这意味着您可以关闭命令窗口并且该进程继续在后台运行?当我使用Start-Process 尝试它时,我无法在关闭父命令窗口后使该过程持续存在。
  • @Dave 我将Thread.sleep(5000) 和另一组println 语句添加到我的示例Java 程序中,使用我在PowerShell 提示符下的回答中的Start-Process 语句启动了一个PowerShell 脚本,然后关闭了之后立即提示(在 Java 程序完成之前)。 Java 程序继续运行,并按照应有的方式用 2 行填充每个输出文件。我使用 CMD 提示符重复了测试,通过 powershell -File script.ps1 运行 PowerShell 脚本,结果相同。
  • 这与我正在寻找的内容非常接近。它可以工作,但是当 Java 程序运行时,会有一个辅助(空白)窗口显示它连接到哪个窗口。我可以使用-WindowStyle 选项抑制它,但我无法重定向输出流。对于Start-Process cmdlet,-WindowStyle 似乎与-RedirectStandardOutput-RedirectStandardError 位于不同的选项集中,因此无法组合这些选项。有没有办法隐藏那个窗口?
【解决方案2】:

考虑为此使用任务调度程序。定义一个任务并在没有任何触发器的情况下设置它。这将允许您简单地“运行”(手动触发)任务。

您可以使用 ScheduledTasks powershell 模块设置和/或触发计划任务,也可以使用 GUI。

【讨论】:

  • 谢谢,@use。我没有意识到可以通过 PowerShell 安排任务,所以我会研究一下。可以将任务设置为一次性任务而不是持久性或重复性吗?
【解决方案3】:

这是一篇旧帖子,但由于我运行良好,因此认为它可能有助于分享。调用“java”而不是“javaw”可能是您的问题。通过powershell使用我的JEdit java程序运行它来启动它。

#Requires -Version 3.0
$MyDriveRoot = (Get-Location).Drive.Root
$JEditDir = $($mydriveroot + "jEdit") ;# Should be C:\jEdit or wherever you want. JEdit is a sub-directory.
$jEdit = $($JEditDir + "\jedit.jar" )
$jEditSettings = $($JEditDir + "\settings")
$JEditLogs = $($JEditDir + "\logs")

Start-Process -FilePath javaw -ArgumentList ( '-jar',"$jEdit", '-settings="$JEditSettings"' ) -RedirectStandardOutput "$JEditLogs\console.out" -RedirectStandardError "$JEditLogs\console.err"

你可以把它变成一个小函数,然后变成一个别名,以便在 Powershell 中轻松启动。

If ( ( Test-Path $jedit) ) {
    Function Start-JEdit() {
        Start-Process -FilePath javaw -ArgumentList ( '-jar',"$jEdit", '-settings="$($mydriveroot + "jEdit\settings")"' ) -RedirectStandardOutput "$JEditLogs\console.out" -RedirectStandardError "$JEditLogs\console.err"
    }
New-Alias -Name jedit  -Force Start-JEdit  -Description "Start JEdit programmers text editor" 
}

【讨论】:

  • 我最终确实解决了这个问题,是的,“javaw”原来是解决方案。我很快就转移到其他项目,以至于我忘了回来发布更新。谢谢!
  • 这不是启动分离进程的通用解决方案。它适用于 OP 的情况,但不适用于要启动的程序不是 Java 程序的其他情况。您能否扩展您的答案以包含通用解决方案?
  • 我理解您的观点 Serid,但它也适用于一般情况。它变得更加复杂,但“启动进程”将允许用户进入后台并将控制台 I/O 转换为变量。这不是 SO 人们习惯的平均“代码字节”。
【解决方案4】:

【讨论】:

  • 注意:这仅适用于 Linux(问题标记为“windows”)。
【解决方案5】:

老问题,但由于我的目标相同,我使用@use的答案来实现它。

这是我的代码:)

$NAME_TASK = "myTask"
$NAME_TASKPATH = "\myPath\"

if ($args[0] -eq "-task") {
  # Code to be run "detached" here...
  Unregister-ScheduledTask -TaskName $NAME_TASK -TaskPath $NAME_TASKPATH -Confirm:$False
  Exit
}

$Task = (Get-ScheduledTask -TaskName $NAME_TASK -TaskPath $NAME_TASKPATH -ErrorAction 'SilentlyContinue')
if ($Task) {
  Write-Host "ERR: Task already in progress"
  Exit 1
}

$A = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-ExecutionPolicy bypass -NoProfile -Command ""$PSCommandPath -task $args"""
Register-ScheduledTask -TaskName $NAME_TASK -TaskPath $NAME_TASKPATH -Action $A | Start-ScheduledTask

【讨论】:

    猜你喜欢
    • 2015-02-15
    • 1970-01-01
    • 2020-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多