When I attempt to exit ruby processes, namely, specs, rails console, and binding.pry calls, there are two options: ctrl+c, ctrl+z, or if things are really stuck, open a separate tab and killall ruby. However, when I ctrl+c the first time, the terminal outputs Exiting... Interrupt again to exit immediately. but hangs permanently. If I ctrl+c again to force exit, it successfully exits. However, from that point on, I can no longer see what I'm typing into the shell. So if I type ls, the line will still appear blank, but if I hit enter, it will successfully execute the ls command.
When I ctrl+z, it manages to stop the process successfully. However, after doing this a few times, I wind up with a bunch of ruby processes running, which seem to block running new ruby processes. In this scenario, killall ruby does nothing (nor does any derivative such as looking up by pid). I have to open activity monitor (mac) and force quit each proc individually.
Any ideas how I managed to get myself into this/how to resolve it?
Killing all your Ruby instances is a shotgun approach; you might hit targets you didn't intend, so I suggest to avoid it.
When your shell doesn't show what you're typing any more you need to (blindly) enter reset to reset the terminal.
Ctrl+Z doesn't kill your process, it just get's suspended. You should get an output that tells you a job number, like:
[1] + 95295 suspended man reset
Here, 1 is the job number. You can then resume the command by typing fg %<jobnumber>, in this example fg %1. Or you can kill it with kill -9 %<jobnumber>, like kill -9 %1.
Related
I'm trying to achieve the following:
from a fish script, open a PDF reader as a background job. Once it is opened, spawn another fish process (that runs an infinite while loop), also as a background job.
Next, open an editor (neovim) and allow it to take control of the running terminal. Once neovim terminates, also suspend the previous 2 background jobs (mupdf and the other fish process).
My current attempt looks something along the lines of:
mupdf $pdfpath &
set pid_mupdf $last_pid
fish -c "while inotifywait ...; [logic to rebuild the pdf file..]; end" &
set pid_sub $last_pid
nvim $mdpath && kill -2 $pid_mudf $pid_sub
First I open mupdf as a background job and save its PID in a variable. Next I spawn the other fish process, also as a background job, and I save its PID as well.
Next I run nvim (but not as a background job, as I intend to actually control it), and after it is terminated by the user, I gracefully kill the previous 2 background jobs.
However this doesn't work as intended.
mupdf and the second fish process open successfully, and so does nvim, but it quickly closes after around half a second, after which I get the following in the controlling terminal window: image (bote is just the filename of the script from which the lines above originate)
The 2 background processes stay running after that and I have to kill them manually.
I understand that the script is sent a SIGHUP because the controlling terminal now executes another application (neovim), but why does neovim close after that?
I also tried disowning the background processes after they're spawned but that didn't help.
How would I solve this issue?
The problem is that $last_pid, in fish 3, and %last, in fish 2, doesn't work by default in scripts. See https://github.com/fish-shell/fish-shell/issues/5036. You can "fix" this by putting status job-control full at the top of the script or using the (jobs -lp) hack that Glenn mentioned.
Regarding the background process remaining running... I can't reproduce that. It works for me. However, note that your nvim && kill will only run the kill if nvim exits with a status of zero. If you always want the kill to be run you should just unconditionally execute it. Also, your use of signal two (SIGINT) should produce the desired result but is unusual. You should use kill -15 or just omit the signal in which case it defaults to 15 (SIGTERM).
You're getting the PID incorrectly. The $pid_mudf and $pid_sub variables are empty. You want
set pid_mupdf (jobs -lp)
I'm trying to use a shell script to start a command. I don't care if/when/how/why it finishes. I want the process to start and run, but I want to be able to get back to my shell immediately...
You can just run the script in the background:
$ myscript &
Note that this is different from putting the & inside your script, which probably won't do what you want.
Everyone just forgot disown. So here is a summary:
& puts the job in the background.
Makes it block on attempting to read input, and
Makes the shell not wait for its completion.
disown removes the process from the shell's job control, but it still leaves it connected to the terminal.
One of the results is that the shell won't send it a SIGHUP(If the shell receives a SIGHUP, it also sends a SIGHUP to the process, which normally causes the process to terminate).
And obviously, it can only be applied to background jobs(because you cannot enter it when a foreground job is running).
nohup disconnects the process from the terminal, redirects its output to nohup.out and shields it from SIGHUP.
The process won't receive any sent SIGHUP.
Its completely independent from job control and could in principle be used also for foreground jobs(although that's not very useful).
Usually used with &(as a background job).
nohup cmd
doesn't hangup when you close the terminal. output by default goes to nohup.out
You can combine this with backgrounding,
nohup cmd &
and get rid of the output,
nohup cmd > /dev/null 2>&1 &
you can also disown a command. type cmd, Ctrl-Z, bg, disown
Alternatively, after you got the program running, you can hit Ctrl-Z which stops your program and then type
bg
which puts your last stopped program in the background. (Useful if your started something without '&' and still want it in the backgroung without restarting it)
screen -m -d $command$ starts the command in a detached session. You can use screen -r to attach to the started session. It is a wonderful tool, extremely useful also for remote sessions. Read more at man screen.
I've seen monitoring programs either in scripts that check process status using 'ps' or 'service status(on Linux)' periodically, or in C/C++ that forks and wait on the process...
I wonder if it is possible to use bash with trap and restart the sub-process when SIGCLD received?
I have tested a basic suite on RedHat Linux with following idea (and certainly it didn't work...)
#!/bin/bash
set -o monitor # can someone explain this? discussion on Internet say this is needed
trap startProcess SIGCHLD
startProcess() {
/path/to/another/bash/script.sh & # the one to restart
while [ 1 ]
do
sleep 60
done
}
startProcess
what the bash script being started just sleep for a few seconds and exit for now.
several issues observed:
when the shell starts in foreground, SIGCHLD will be handled only once. does trap reset signal handling like signal()?
the script and its child seem to be immune to SIGINT, which means they cannot be stopped by ^C
since cannot be closed, I closed the terminal. The script seems to be HUP and many zombie children left.
when run in background, the script caused terminal to die
... anyway, this does not work at all. I have to say I know too little about this topic.
Can someone suggest or give some working examples?
Are there scripts for such use?
how about use wait in bash, then?
Thanks
I can try to answer some of your questions but not all based on what I
know.
The line set -o monitor (or equivalently, set -m) turns on job
control, which is only on by default for interactive shells. This seems
to be required for SIGCHLD to be sent. However, job control is more of
an interactive feature and not really meant to be used in shell scripts
(see also this question).
Also keep in mind this is probably not what you intended to do
because once you enable job control, SIGCHLD will be sent for every
external command that exists (e.g. every time you run ls or grep or
anything, a SIGCHLD will fire when that command completes and your trap
will run).
I suspect the reason the SIGCHLD trap only appears to run once is
because your trap handler contains a foreground infinite loop, so your
script gets stuck in the trap handler. There doesn't seem to be a point
to that loop anyways, so you could simply remove it.
The script's "immunity" to SIGINT seems to be an effect of enabling
job control (the monitor part). My hunch is with job control turned on,
the sub-instance of bash that runs your script no longer terminates
itself in response to a SIGINT but instead passes the SIGINT through to
its foreground child process. In your script, the ^C i.e. SIGINT
simply acts like a continue statement in other programming languages
case, since SIGINT will just kill the currently running sleep 60,
whereupon the while loop will immediately run a new sleep 60.
When I tried running your script and then killing it (from another
terminal), all I ended up with were two stray sleep processes.
Backgrounding that script also kills my shell for me, although
the behavior is not terribly consistent (sometimes it happens
immediately, other times not at all). It seems typing any keys other
than enter causes an EOF to get sent somehow. Even after the terminal
exits the script continues to run in the background. I have no idea
what is going on here.
Being more specific about what you want to accomplish would help. If
you just want a command to run continuously for the lifetime of your
script, you could run an infinite loop in the background, like
while true; do
some-command
echo some-command finished
echo restarting some-command ...
done &
Note the & after the done.
For other tasks, wait is probably a better idea than using job control
in a shell script. Again, it would depend on what exactly you are trying
to do.
In a ruby script, I start more virtual shells, each managed by a shell manager object, like so:
#shell = PTY.spawn 'env PS1="\w>" TERM=dumb COLUMNS=63 LINES=21 sh -i'
At some later point in time, I would like to destroy this instance and also kill the associated shell process. Sadly, I can't get anything to work properly. Here's what I tried, in order of probability to work:
Nothing, that is, expecting the shell proc gets closed when the managing object gets destroyed.
Killing all processes running on the shell (this works) with the kill command, and then killing the shell itself with system("kill #{#shell[2]"). This has no effect.
Using -9 in the above. This leaves the shell process defunct.
All the shells get closed when the ruby program exits, but I want to kill them while keeping the program running. Anyone encounter something like this before?
The problem is zombies. Yes, really.
All Unix-style kernel's leave the process around until someone waits for it. (That's in order to keep track of the PID, the exit status, and a bit of other stuff.) They are called zombies and have a Z state in the ps(1) listing. You can't kill them, because they are already dead. They go away when you wait for them.
So here is how to clean up your #shell object:
#shell[0].close
#shell[1].close
begin
Process.wait #shell[2]
rescue PTY::ChildExited
end
You may not need the rescue block depending on whether you have higher level layers catching exceptions too broadly. (Sigh, like my irb.)
By the way, the reason your process finally vanished when the Ruby program exited is because then the zombie also became an orphan (no parent process) and either the shell or init(8) will eventually wait for all orphans.
I wanted to know why i am seeing a different behaviour in the background process in Bash shell
Case 1: Logged in to Unix server using Putty(SSH)
By default it uses csh shell
I changed to bash shell
typed sleep 2000 &
press enter
It gave me the job number. Now i killed my session by clicking the x in the putty window
Now open another session and tried to lookup the process..the process died.
Case 2:Case 1: Logged in to Unix server using Putty(SSH)
By default it uses csh shell
I changed to bash shell
vi mysleep.sh
sleep 2000 & Saved mysleep.sh
./mysleep.sh
Diff here is..instead of executing the sleep command directly i am storing the sleep command in a file and executing the file.
Now i killed my session by clicking the x in the putty window
Now open another session and tried to lookup the process..the process is still there
Not sure why this is happening. I thought i need to do disown in bash to run the process even after logging out.
One diff i see in the parent process id..In the second case..the parent process id for the sleep 2000 becomes 1. Looks like as soon as process for mysleep.sh died the kernel assigned the parent process to 1.
The difference here is indeed the intervening process.
When you close the terminal window, a HUP signal (related to "nohup" as an0nymo0usc0ward mentioned) is sent to the processes running in it. The default action on receiving HUP is to die - from the signal(3) manpage,
No Name Default Action Description
1 SIGHUP terminate process terminal line hangup
In your first example, the sleep process directly receives this HUP signal and dies because it isn't set to do anything else. (Some processes catch HUP and use it to perform some action, e.g. reread some configuration files)
In the second example, the shell process running your shell script has already died, so the sleep process never gets the signal. In UNIX, every process must have a parent process due to the internals of how the wait(2) family of calls works and indeed processes in general. So when the parent process dies, the kernel gives it to init (pid 1, as you note) as a foster child.
Orphan process (on wikipedia) has some more information available about it, also see Zombie process for some additional technical details.
Already running process?
^z
bg
disown %<jobid>
New process/script (on local machine's console)?
nohup script.sh &
New process/script (on remote machine's console)?
Depending on your need,
there are two options [ there will be more ;-) ]
ssh remotehost 'nohup /path/to/script.sh </dev/null > nohup.out 2>&1 &'
OR
use 'screen'
Try "nohup cmd args..."
Steven's answer is correct, but I'd like to highlight the tricky part here again:
=> Using a bash script that just executes sleep in the background
The effect of this is that the "script" exits almost immediately (since it's done all its commands). However, it did create a child process (sleep) during its lifetime. The effect of this is that:
The "script" cannot be the parent anymore, and sleep is orphaned to init (which shows nicely in a pstree)
The bash shell where you started the script from has no underlying jobs anymore
Note that this stuff all happens when you executed the script, and has nothing to do with any ssh logout/putty closing.
When you then finally close your putty session, bash receives a "SIGHUP", but doesn't forward it to any other process (since there are no jobs left)
In the other case, bash did still have a job left, which it then sent the SIGHUP to, causing it to end (as you noticed)
Hope this helps