24

I have a Java program which I would like to launch as a background process from a PowerShell script, similar to the way a daemon runs on Linux. The PowerShell script needs to do a couple of things:

  1. Run the program as a separate and detached process in the background, meaning the parent window can be closed and the process keeps running.
  2. Redirect the program's standard output and standard error to files.
  3. Save the PID of the background process to a file so it can be terminated later by another script.

I have a shell script on Linux which starts the program like so:

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

I'm hoping to replicate the same behavior on Windows using a PowerShell script. I have tried using Start-Process with various combinations of options, as well as creating System.Diagnostics.ProcessStartInfo and System.Diagnostics.Process objects, but so far I am not having any luck. PowerShell starts the program as a background process, but the program abruptly terminates when the DOS window which started the PowerShell session is closed. I would like it to start in the background and be independent of the command window which started it.

The output redirection has also been troublesome, as it seems that the output and error streams can only be redirected in the process is being run in the same window (e.g., using -NoNewWindow).

Is this sort of thing possible in PowerShell?

Dave
  • 505
  • 1
  • 5
  • 16
  • 1
    You need to have Powershell v3 or higher, and use the `Start-PSSession` cmdlet – Eris Jul 29 '14 at 19:32
  • Thanks @Eris. I'm really hoping to find a way to do this in PowerShell 2.0+ though. Is it simply not possible? Seems a very simple thing to do (bash can do it in one line). – Dave Jul 29 '14 at 19:37
  • Actually, on windows, bash doesn't do what you want either. If I run "/c/windows/notepad.exe &" in bash, the bash process doesn't exit until notepad is done, OR notepad is killed when the bash window is closed. – Eris Jul 29 '14 at 19:45
  • 1
    Since you are using Powershell 2, this is a duplicate of http://stackoverflow.com/questions/185575/powershell-equivalent-of-bash-ampersand-for-forking-running-background-proce – Eris Jul 29 '14 at 19:48
  • 1
    I saw that post in my previous Googlings, but I don't think the OP of that question was looking to close the original command window and still keep the background job running. When you do this on Linux, the "orphaned" background process simply becomes a child of the system's process. It seems that Windows does not like this kind of thing, though. Does it have any mechanism in place to keep processes running after their parents are closed? – Dave Jul 29 '14 at 20:03
  • 2
    http://stackoverflow.com/questions/23091731/how-can-i-launch-cmd-files-on-a-remote-machine ... Use WMI to Create a detached process. GL – Cole9350 Jul 29 '14 at 20:16
  • 2
    You do not need Powershell to do that, just use the CMD.EXE (DOS session no longer means anything for years now) command called _Start_. Once you do that, the result will be a detached process, but not a deamon if you close your interactive session the detached process will die. The real equivalent to a deamon is a service on Wndows OS. – JPBlanc Jul 30 '14 at 05:01
  • 1
    @JPBlanc Actually, `START /B` does exactly what I need, except that I'm not able to capture the PID of the forked process to a file so it can be terminated later. Sorry, I should have listed that as a requirement in the original post. I will edit to make it more clear. – Dave Jul 30 '14 at 15:02

7 Answers7

30

Use jobs for this:

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

Another option would be Start-Process:

Start-Process java -ArgumentList '-jar', 'MyProgram.jar' `
  -RedirectStandardOutput '.\console.out' -RedirectStandardError '.\console.err'
Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
  • Thanks, @Ansgar. My understanding from the documentation was that Start-Job only worked for launching other PowerShell scripts, but that's probably wrong and I will give your suggestion a try. Start-Process doesn't work because it can't start the process in a new window and also redirect output streams in the same invocation, it seems to be one or the other. – Dave Jul 30 '14 at 15:00
  • @Dave `Start-Job` can run scriptblocks, which may contain any kind of PowerShell code, including calls to external commands. As for `Start-Process`: it worked just fine when I tested it with a simple application running `System.out.println("foo"); System.err.println("bar");` (after I fixed the typo in `-RedirectStdardError`). – Ansgar Wiechers Jul 30 '14 at 17:45
  • 6
    Did it start as a detached process, meaning you could close the command window and the process kept running in the background? When I try it with `Start-Process` I am not able to make the process persist after I close the parent command window. – Dave Jul 30 '14 at 19:09
  • 1
    @Dave I added `Thread.sleep(5000)` and another set of `println` statements to my sample Java program, launched a PowerShell script with the `Start-Process` statement from my answer from a PowerShell prompt, and closed the prompt right afterwards (before the Java program finished). The Java program continued to run and filled each output file with 2 lines, as they should. I repeated the test with a CMD prompt, running the PowerShell script via `powershell -File script.ps1`, with the same result. – Ansgar Wiechers Jul 30 '14 at 19:45
  • This is darn close to what I'm looking for. It works, but when the Java program runs there is a secondary (blank) window showing which it is attached to. I can suppress it with the `-WindowStyle` option, but then I cannot redirect the output streams. It seems that `-WindowStyle` is in a different option set than `-RedirectStandardOutput` and `-RedirectStandardError` for the `Start-Process` cmdlet, and therefore these options cannot be combined. Is there a way to hide that window? – Dave Jul 30 '14 at 19:55
  • @Dave I cannot reproduce your problem. Adding `-WindowStyle Hidden` to the statement works just fine for me. – Ansgar Wiechers Jul 30 '14 at 20:02
  • I'm using PowerShell 2 on Windows 7 and I get: `Start-Process : Parameter set cannot be resolved using the specified named parameters. At C:\checkvalvechatrelay-1.1.0\start.ps1:100 char:22 + $proc = Start-Process <<<< + CategoryInfo : InvalidArgument: (:) [Start-Process], ParameterBindingException + FullyQualifiedErrorId : AmbiguousParameterSet,Microsoft.PowerShell.Comma nds.StartProcessCommand` – Dave Jul 30 '14 at 20:33
  • @Dave I'm using PowerShell v3 on Win7, so apparently you need to either use `Start-Job` or upgrade PowerShell (which I'd recommend anyway). – Ansgar Wiechers Jul 31 '14 at 07:21
6

Consider using the task scheduler for this. Define a task and set it without any triggers. That will allow you to simply "Run" (manually trigger) the task.

You can set up and/or trigger scheduled tasks using the ScheduledTasks powershell module, or you can use the GUI.

use
  • 131
  • 2
  • Thanks, @use. I did not realize that it was possible to schedule tasks via PowerShell, so I will look into that. Can the task be set as a one-off task and not be persistent or recurring? – Dave Jul 30 '14 at 15:06
4

This is an old post but since I have it working fine thought it might help to share. Its the call to 'java' instead of 'javaw' that is likely your issue. Ran it out myself using my JEdit java program through powershell to launch it.

#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"

Which you can turn into a little function and then an alias to make it easy to launch in 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" 
}
Ernie M.
  • 82
  • 2
  • I eventually did solve this, and yes "javaw" turned out to be the solution. I moved on to other projects so quickly that I forgot to come back and post an update. Thanks! – Dave Dec 22 '20 at 19:27
  • This is not a general solution for starting a process detached. It will work in OP's case but it is not applicable in other cases where the program to be started is not a Java program. Could you please expand your answer to include a general solution? – Serid Jan 13 '21 at 17:27
  • I understand your point Serid, yet it works in a general case as well. It gets more elaborate but 'Start-Process' will allow a user to background and take console I/O into a variable. It is not an average 'code byte' that SO folks are used to. – Ernie M. Jul 07 '21 at 23:14
2

Try this with PowerShell:

Start-Process cmd -Args /c,"java -jar MyProgram.jar" `
  -WindowStyle Hidden -RSI console.out -RSE console.err

OR

Start-Process cmd -Args /c,"java -jar MyProgram.jar >console.out 2>console.err" `
  -WindowStyle Hidden

This will start a detached cmd window that is hidden, and will redirect the std streams accordingly.

Vopel
  • 662
  • 6
  • 11
0

Old question, but since I had the same goal, I used answer from @use to acheive it.

So here is my code :)

$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
0

To complete @Ansgar's answer, and provide a complete one-liner that you can run in your terminal without having to modify the format at all:

Here is the command you can launch if you only want to launch a process and detach it (just like if you were e.g. launching it from the Start Menu) and don't care about the outputs (stdout and stderr) =>

Start-Job -ScriptBlock { & command >$null 2>$null }
PS: Text in orange needs to be replaced with your own values
adamency
  • 682
  • 6
  • 13
-3

The solution is to combine Start-Process with nohup:

https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/start-process?view=powershell-7.3#example-9-create-a-detached-process-on-linux

(Note: This is NOT for Windows.)

Vopel
  • 662
  • 6
  • 11