What is the equivalent of 'nohup' in linux PowerShell?
Asked Answered
S

1

3

I very well know existence of duplicate question. But that question is marked answered and I don't think it at all answers the original question.

The $ nohup command keeps executing command even if the parent process (shell) of nohup dies. More info here: What's the difference between nohup and ampersand.

In case of Win32 api, I see line When the system is terminating a process, it does not terminate any child processes that the process has created. Terminating a process does not generate notifications for WH_CBT hook procedures.. This is the desired behaviour I am asking about. This works with win32 powershell automatically. But with linux powershell's Start-Job it's merely background job in the powershell context in the sense that it gets killed when powershell gets killed.

Example:

➜  ~ powershell-preview -c "Start-Job { /bin/sleep 1000 }"

Id     Name            PSJobTypeName   State         HasMoreData     Location             Command
--     ----            -------------   -----         -----------     --------             -------
1      Job1            BackgroundJob   Running       True            localhost             /bin/sleep 1000

➜  ~ ps ux  | grep sleep
mvaidya   166986  0.0  0.0   9032   664 pts/11   S+   18:59   0:00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn --exclude-dir=.idea --exclude-dir=.tox sleep
Superphysical answered 6/11, 2020 at 2:3 Comment(1)
As an aside: even on Windows the child processes that run jobs created with Start-Job are killed when the launching shell exits.Asel
A
4

Using Start-Job or PowerShell v6+'s & background operator isn't an option, because terminating the job will also terminate child processes launched from it, which jobs are - both on Windows and on Unix-like platforms.

However, you can achieve nohup-like behavior via the Start-Process cmdlet:


On Unix-like platforms, you can combine Start-Process with nohup:
The following example launches a background PowerShell instance that stays alive even after you close the launching terminal; it emits a . every second, and nohup collects both stdout and stderr output in file nohup.out in the current directory, appending to such a file if it already exists:

# Runs for 2 minutes and appends both stdout and stderr output to ./nohup.out
Start-Process nohup 'pwsh -nop -c "1..120 | % { write-host . -nonewline; sleep 1 }"'

Caveat: As of PowerShell 7.2, running this command via a SSH connection only works if the connection was initiated via ssh, not via PowerShell's own SSH-based remoting cmdlets, such as Invoke-Command and Enter-PSSession[1].

For instance - assuming that the user's default shell on the target computer is pwsh (PowerShell), the following command could be used; note that option -t is needed in order to allocate a pseudo terminal (pty), so that nohup thinks its stdout is connected to a terminal and therefore sends its output to file .\nohup.out):

ssh -t <user>@<host> 'Start-Process nohup ''pwsh -nop -c \"1..120 | % { write-host . -nonewline; sleep 1 }\"'''

Note the surprising need for \" rather than just ", required to compensate for PowerShell's still-broken argument-passing to external programs as of PowerShell 7.1 - see this answer.

By contrast, if the user's default shell on the target computer is a POSIX-compatible shell such as bash, use the following (run from PowerShell):

# IMPORTANT: If the target machine runs macOS rather than Linux, use the
#            `pwsh`'s *full path*, which is `/usr/local/bin/pwsh` by default.
ssh -t <user>@<host> 'nohup pwsh -nop -c ''1..120 | % { write-host . -nonewline; sleep 1 }'' & whoami >/dev/null'

Note the use of dummy command whoami >/dev/null after submitting the background job ( &), which is seemingly required to ensure that nohup gets enough time to actually launch its target command.


On Windows, where Start-Process by default creates an independent process in a new console window, you can use -WindowStyle Hidden to launch that process in a hidden window that will remain alive independently of the launching shell.

# Runs for 2 minutes and appends success output to ./nohup.out
Start-Process -WindowStyle Hidden pwsh '-nop -c "1..120 | % { Add-Content -nonewline nohup.out -Value .; sleep 1 }"'

Caveat: This does not work as-is work remoting:

  • In a remoting sessio, Start-Process cannot launch the process in a separate (hidden) window this way, so on exiting the remoting session the launched process is terminated as well.

  • However, you can create a disconnected session that remains alive on the target computer until it is explicitly deleted (or until the target computer reboots); alternatively, create a regular (connected) session explicitly and keep it as long as needed - though if the calling computer reboots before the task is completed, the task is again terminated (unless you explicitly disconnect the session first).

An example, using Invoke-Command's -InDisconnectedSession switch:

# Launch the command remotely, in a disconnected session, and
# return a session-information object:
$disconnSess = Invoke-Command -Computer <host> -InDisconnectedSession {
  Start-Process -WindowStyle Hidden pwsh '-nop -c "1..120 | % { Add-Content -nonewline nohup.out -Value .; sleep 1 }"'
}

# ... Let the command finish

# Remove the session again
Remove-PSSession $disconnSess

[1] These cmdlets do not allocate a pseudo terminal (pty), the absence of which causes nohup to print its output to stdout and stderr rather than to file .\nohup.out. Additionally, this unexpected, directly printed output can cause the remote PowerShell session to crash.

Asel answered 6/11, 2020 at 2:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.