How to open a child shell in a new tab in bash so that I have a parent and a child shell running simultaneously? - bash

I am trying to study the about shells and I was exploring about how variables are shared among child and parent shells. I am able to open a child shell in the same tab as the parent shell. However, at this time, I am not being able to make changes in the variables in the parent shell since the child shell is running. So I needed to open the child shell in a new tab instead, so that I can work on both the child and parent shells simultaneously. Is this even possible?

You can stop the child shell then tell it to run in the background with "bg". The echo $$ command returns the shells pid, and the kill -19 sends the stop signal to the pid.
$ echo $$
19863
$ kill -19 19863
[1]+ Stopped sh
$ bg
[1]+ sh &
$
After you've made your changes to the parent shell environment variables you can "fg" to foreground the child shell.
You can also look into "screen" and "xterm &" these are 2 other ways to launch a sub shell and easily interact with the parent shell.

Related

Bash: wait for the child process spawned before exec'ing bash

Bash can use wait to wait for the processes it started directly. However, if the process forks a child, and then execs bash (that is, parent turns into bash), the newly exec'd Bash process cannot wait for the "inherited" child. Here is the minimal reproduction:
#/bin/bash
sleep inf &
pid=$!
exec bash -c "wait $pid;"'echo This shell is $$; sleep inf'
which gives this output:
$ bash test.sh
bash: wait: pid 53984 is not a child of this shell
This shell is 53983
The pstree, however, shows that the child pid is indeed the child of the shell:
$ pstree -p 53983
bash(53983)─┬─sleep(53984)
└─sleep(53985)
It seems that Bash tracks the spawned processes internally, and consults this list rather than calling waitpid(2) directly (zsh has the same problem, but ksh works as expected).
Is there any way to workaround this behavior, and have Bash add the "inherited" child to its internal structures?
I could not reproduce it, but I wrote a script that shows that this behaviour is consistent in at least 6 well maintained shells (including the mentioned ksh).
As you can see in the report, all shells won't list the first sleep job in the replaced shell, only the new one created after the exec call.
When invoking exec the new shell does not inherit the job list managed by the replaced one. It seems an intended behaviour but I could not find it anywhere in the POSIX specification.

How child processes gets terminated when the parent is killed using SIGINT?

#!/usr/bin/env bash
for i in $(seq 1 $1);
do
./extended&
done
wait
This is my bash script and I execute the extended binary as many times as specified in command line argument. When I kill the bash script using SIGINT the child processes also killed. I've called wait in the bash script I couldn't figure how the child processes are killed. I know that wait will make the parent to wait till child terminates.
bash sends a SIGHUP (hang-up signal) to all children on exit by default. If you don't want this behaviour use disown -h
From man bash:
To prevent the shell from sending the signal to a particular job, it should be removed from the jobs table with the disown builtin or marked to not receive SIGHUP
using disown -h.

Does a bash subshell spawn a new `bash` process?

I am reading The TTY demystified. In the "Jobs and sessions" section there is an example of a user using an xterm:
$ cat
hello
hello
^Z
[1]+ Stopped cat
$ ls | sort
And there is a table listing the processes involved: xterm, bash (child of the xterm), and the three last processes (cat, ls and sort) all have the same PPID (parent process ID) -- they are all children of the same bash process.
Now, I know that pipelines in bash are executed in subshells. I have always thought that this subshell thing meant that there was an extra bash process for each subshell. My question is: shouldn't there be another two bash processes, both children of the first bash, and then ls would be a child of the first bash, and sort would be a child of the second bash? Is the table in the article simplified, or is my understanding of subshells wrong?
Programs are executed in child processes, but these are not subshells. The shell forks a child, redirects standard input/output/error as necessary, and then immediately calls execv() to execute the program.
For a very brief period the child process is still running bash, but we don't consider this a subshell because it's not doing any shell command processing -- that was all done in the original shell, and the child is just starting up the external program (as if via an explicit exec for commands like ls).
In the case of a pipeline, if any of the commands are shell built-ins, they run in a subshell. So if you do:
ls | read var
it will create two child processes. One child will run ls, the other will be a subshell executing read var.
Invoking an executable, whether directly or via a pipe, does not spawn a subshell. Only explicitly invoking it within a subshell (via (...), $(...), and so on) does so.

Exit zsh, but leave running jobs open?

Just switched from bash to zsh.
In bash, background tasks continue running when the shell exits. For example here, dolphin continues running after the exit:
$ dolphin .
^Z
[1]+ Stopped dolphin .
$ bg
[1]+ dolphin . &
$ exit
This is what I want as the default behavior.
In contrast, zsh's behavior is to warn about running jobs on exit, then close them if you exit again. For example here, dolphin is closed when the second exit-command actually exits the shell:
% dolphin .
^Z
zsh: suspended dolphin .
% bg
[1] + continued dolphin .
% exit
zsh: you have running jobs.
% exit
How do I make zsh's default behavior here like bash's?
Start the program with &!:
dolphin &!
The &! (or equivalently, &|) is a zsh-specific shortcut to both background and disown the process, such that exiting the shell will leave it running.
From the zsh documentation:
HUP
... In zsh, if you have a background job running when the shell exits, the shell will assume you want that to be killed; in this case it is sent a particular signal called SIGHUP... If you often start jobs that should go on even when the shell has exited, then you can set the option NO_HUP, and background jobs will be left alone.
So just set the NO_HUP option:
% setopt NO_HUP
I have found that using a combination of nohup, &, and disown works for me, as I don't want to permanently cause jobs to run when the shell has exited.
nohup <command> & disown
While just & has worked for me in bash, I found when using only nohup, &, or disown on running commands, like a script that calls a java run command, the process would still stop when the shell is exited.
nohup makes the command ignore NOHUP and SIGHUP signals from the shell
& makes the process run in the background in a subterminal
disown followed by an argument (the index of the job number in your jobs list) prevents the shell from sending a SIGHUP signal to child processes. Using disown without an argument causes it to default to the most recent job.
I found the nohup and disown information at this page, and the & information in this SO answer.
Update
When I originally wrote this, I was using it for data processing scripts/programs. For those kinds of use cases, something like ts (task-spooler), works nicely.
I typically use screen for keeping background jobs running.
1) Create a screen session:
screen -S myScreenName
2) Launch your scripts,services,daemons or whatever
3) Exit (detach) screen-session with
screen -d
or
shortcut ALT+A then d
After few hundreds of years - if you want to resume your session (reattach):
screen -r myScreenName
If you want to know if there's a screen-session, its name and its status (attached or detached):
screen -ls
This solution works on all terminal interpreters like bash, zsh etc.
See also man screen

How can a background bash script exit the running shell?

Running a bash script in the background with job control enabled and stdin closed will exit the PARENT shell. How can that happen?
To demonstrate make this background_bash_script:
#!/bin/bash
set -m
ruby -e "puts :here"
Then run it in bash - it will exit the shell you ran it in. The ruby command does not matter although it appears it must be a command and not a bash built-in (for example awk --version works but true does not). To get a better look I've been running it in yet another instance of bash. A full session looks like this.
parent: PS1='child: ' bash
child: ./background_bash_script <&- &
[1] 3893
child: here
exit
parent:
Confusing!
What seems like is happening is that after set -m is run in the script, the next command that is run is forced to be in the foreground process group, which takes the original shell out of the foreground process group. Once that process exits, the shell running the script is now in the foreground process group, but once that shell exits, the original shell doesn't put itself back into the foreground process group because it ran the script in the background. So you now have an interactive shell that is in a background process group.
You can see some weird behavior here if you put a sleep at the end of your script so that it doesn't exit immediately. When you run the script in the background you get the terminal prompt back, but now your interactive shell isn't in the foreground process group! As soon as you try to type anything the shell exits. I'm not sure exactly what mechanism causes the exit. Since the shell is in the background, any attempts to read or write characters to the terminal should result in SIGTTIN OR SIGTTOU, but these signals don't cause the shell to exit in my tests.

Resources