How can I run a command which will survive terminal close?
Sometimes I want to start a process and forget about it. If I start it from the command line, like this:
redshift
I can’t close the terminal, or it will kill the process. Can I run a command in such a way that I can close the terminal without killing the process?
One of the following 2 should work:
$ nohup redshift &
or
$ redshift &
$ disown
See the following for a bit more information on how this works:
-
Difference between nohup, disown and & (be sure to read the comments too)
If your program is already running you can pause it with Ctrl-Z
, pull it into the background with bg
and then disown
it, like this:
$ sleep 1000
^Z
[1]+ Stopped sleep 1000
$ bg
$ disown
$ exit
You can run the process like this in the terminal
setsid process
This will run the program in a new session, as explained in my article here.
Good answer is already posted by @Steven D, yet I think this might clarify it a bit more.
The reason that the process is killed on termination of the terminal is that the process you start is a child process of the terminal. Once you close the terminal, this will kill these child processes as well. You can see the process tree with pstree
, for example when running kate &
in Konsole:
init-+
├─konsole─┬─bash─┬─kate───2*[{kate}]
│ │ └─pstree
│ └─2*[{konsole}]
To make the kate
process detached from konsole
when you terminate konsole
, use nohup
with the command, like this:
nohup kate &
After closing konsole
, pstree
will look like this:
init-+
|-kate---2*[{kate}]
and kate
will survive. 🙂
An alternative is using screen
/tmux
/byobu
, which will keep the shell running, independent of the terminal.
I have a script (I called run
) to:
- Run arbitrary commands in the background
- Stop them from being killed with the terminal window
- Suppress their output
- Handles exit status
I use it mainly for gedit
, evince
, inkscape
etc that all have lots of annoying terminal output. If the command finishes before TIMEOUT
, nohup’s exit status is returned instead of zero. Contents of run
:
#!/bin/bash
TIMEOUT=0.1
#use nohup to run the command, suppressing its output and allowing the terminal to be closed
#also send nohup's output to /dev/null, supressing nohup.out
#run nohup in the background so this script doesn't block
nohup "${@}" >/dev/null 2>&1 &
NOHUP_PID=$!
#kill this script after a short time, exiting with success status - command is still running
#this is needed as there is no timeout argument for `wait` below
MY_PID=$$
trap "exit 0" SIGINT SIGTERM
sleep $TIMEOUT && kill $MY_PID 2>/dev/null & #ignore "No such process" error if this exits normally
#if the command finishes before the above timeout, everything may be just fine or there could have been an error
wait $NOHUP_PID
NOHUP_STATUS=$?
#print an error if there was any. most commonly, there was a typo in the command
[ $NOHUP_STATUS != 0 ] && echo "Error ${@}"
#return the exit status of nohup, whatever it was
exit $NOHUP_STATUS
Examples:
>>> run true && echo success || echo fail
success
>>> run false && echo success || echo fail
Error false
fail
>>> run sleep 1000 && echo success || echo fail
success
>>> run notfound && echo success || echo fail
Error notfound
fail
The shell-only way to do all this is to close stdin and background the command:
command <&- &
Then it won’t quit when you quit the shell. Redirecting stdout is a nice optional thing to do.
Disadvantage is that you can’t do this after the fact.
Though all of the suggestions work well, I’ve found my alternative is to use screen
, a program that sets up a virtual terminal on your screen.
You might consider starting it with screen -S session_name
. Screen can be installed on virtually all Linux and Unix derivatives. Hitting Ctrl+A and (lower case) C will start a second session. This would allow you to toggle back and forth between the initial session by hitting Ctrl+A and 0 or the newer session by hitting Ctrl+A and 1. You can have up to ten sessions in one terminal. I used to start a session at work, go home, ssh into my work machine, and then invoke screen -d -R session_name
. This will reconnect you to that remote session.
You can set a process (PID) to not receive a HUP signal upon logging out and closing the terminal session. Use the following command:
nohup -p PID
I prefer:
(applicationName &)
for example:
linux@linux-desktop:~$ (chromium-browser &)
Make sure to use parenthesis when type the command!
Arguably similar to the answer offered by apolinsky, I use a variant on screen
. The vanilla command is like this
screen bash -c 'long_running_command_here; echo; read -p "ALL DONE:"'
The session can be disconnected with Ctrl ACtrl D and reconnected in the simple case with screen -r
. I have this wrapped in a script called session
that lives in my PATH
ready for convenient access:
#!/bin/bash
#
if screen -ls | awk '$1 ~ /^[1-9][0-9]*.'"$1"'/' >/dev/null
then
echo "WARNING: session is already running (reattach with 'screen -r $1')" >&2
else
exec screen -S "$1" bash -c "$@; echo; echo '--------------------'; read -p 'ALL DONE (Enter to exit):'"
echo "ERROR: 'screen' is not installed on this system" >&2
fi
exit 1
This only works when you know in advance you want to disconnect a program. It does not provide for an already running program to be disconnected.
Similarly to other answers posted before, one can transfer a running process to use screen
retrospectively thanks to reptyr and then close the terminal. The steps are described in this post.
The steps to take are:
- Suspend the process
- Resume the process in the background with
bg
disown
the process- Launch a
screen
session - Find the PID of the process
- Use
reptyr
to take over the process