echo "echo in parent process"
bash
echo "echo in subshell"
exit
When I execute this code block form terminal (./test.sh)
I only see "echo in parent process" as output, but I also want to see the output that coming from subshell. What is the way to see the outputs that coming from subshell/child process.
You never see the second command because it hasn't happened yet. Instead, you're just starting bash again, interactively. You'll note you're inside an inner shell now if you type exit manually.
$ ./test.sh
echo in parent process
$ exit
echo in subshell
What you probably mean to do is:
#!/bin/bash
echo "echo in parent process"
bash -c 'echo "echo in child process"'
Related
i want to start programs in a chain, like start a bash script startScript.sh and then in the startScript.sh chain load certain programs.
startScript.sh
./program1 &
#PID=(echo $!)
PID=$!
echo "Wait for 10 seconds here"
if ps -p $PID > /dev/null; then echo "continue"; ./program2; else echo "PID is not running"; exit; fi
echo "Wait for 10 seconds here"
#if program1 is running and program2 is running, then ./program3, else exit.
I tried to find the running pid of program1 with $!, but the problem is that program1 is itself a shell script and they invokes further shell scripts in which I am not interested. Hence $! never gives me the pid of ./program1 but something irrelevant. HOw can I get the PID of program1?
Also how can I get the PID of program1 as well as program2 to see if they are running and then start program3.
The special parameter $! in shell (from man bash):
Expands to the process ID of the job most recently placed into the background, whether executed as an asynchronous command or using the bg builtin (see JOB CONTROL below).
It's important to notice that job control is shell-local (in your case local to the bash interpreter executing your startScript.sh).
That means the pid from below:
#!/bin/bash
./program.sh
pid=$!
will always contain the PID of the program.sh script (i.e. the process executing it, defined with a hashbang, in your case probably another bash process), regardless of the background processes spawned by that child process (remember that $! is local to your script; the $! in the child will be undefined, until a background process is spawned there).
What maybe sidetracked you initially is the line:
PID=(echo $!)
that didn't properly set the PID. The PID was set to an array containing two words (elements): echo and <pid>. You want this:
pid=$!
(and maybe to use lowercase name for the non-global variable pid).
A simple demo:
$ cat script1.sh
#!/bin/bash
./script2.sh &
echo "in script1: $!"
$ cat script2.sh
#!/bin/bash
echo "in script2: $!"
sleep 5 &
echo "in script2 after sleep: $!"
$ ./script1.sh
in script1: 19537
in script2:
in script2 after sleep: 19541
Notice how the $! in initially undefined in script2.sh - if $! were to return the last PID globally, it would be set to PID of script2.sh.
date
ls
exit
On executing this script, the commands date and ls are executed but the terminal doesn't close. Why?
The script will exit only the shell it's running in. When you do ./abc then parent shell forks and execs a subshell in which ./abc is run.
Just to be bit more obvious about subshell exit, consider the following example:
#!/bin/bash
echo hi
(exit)
echo hello
prints both hi and hello. The exit happens in the subshell. So, the script won't exit and hence the last echo hello gets executed.
To see that the script itself exits, try
#!/bin/bash
echo hi
exit
echo hello
Running this script will output just "hi"; the exit command terminates the script (but not the shell running in your terminal) before it reaches echo hello.
I stumbled upon Zenity, a command-line based GUI today. I noticed the there was some syntax of the form ( commands ) | command. Could anyone shed some light on what this is and where I can read more about it?
I found the below script within the docs
(
echo "10" ; sleep 1
echo "# Updating mail logs" ; sleep 1
echo "50" ; sleep 1
echo "This line will just be ignored" ; sleep 1
echo "100" ; sleep 1
) |
zenity --progress \
--title="Update System Logs" \
--text="Scanning mail logs..." \
--percentage=0
The parentheses are delimiting a subshell, which means the commands inside the parens are run in a separate process, and interpreted by a separate instance of the bash interpreter. In this case, it appears they are using a subshell just to group together all the echo and sleep commands so that they can then pipe the combined output of the entire group of commands through zenity. Which makes sense given that the goal in this example is to simulate a progress bar.
You can read more about subshells here: http://tldp.org/LDP/abs/html/subshells.html
The parentheses create a subshell, with all the implications that it has for the current shell.
The subshell cannot change the environment of the parent shell; sometimes, you want a subshell so that you can, say, quickly cd to a different directory without affecting the working directory for the rest of the script
The subshell has a single standard input and a single standard output stream; this is frequently a reason to start a subshell
The parent shell waits while the subshell completes the commands (unless you run it in the background)
If it helps,, think of ( foo; bar ) as a quick way to say sh -c 'foo; bar'.
A related piece of syntax is the brace, which runs a compound command in the current shell, not a subshell.
test -f file.rc || { echo "$0: file.rc not found -- aborting" >&2; exit 127; }
The exit in particular causes the current shell to exit with a failure exit code, while a subshell which exits does not directly affect the rest of the parent shell script.
(Weirdly, POSIX requires a statement terminator before the closing brace, but not before the closing parenthesis.)
Suppose I have test.sh as below. The intent is to run some background task(s) by this script, that continuously updates some file. If the background task is terminated for some reason, it should be started again.
#!/bin/sh
if [ -f pidfile ] && kill -0 $(cat pidfile); then
cat somewhere
exit
fi
while true; do
echo "something" >> somewhere
sleep 1
done &
echo $! > pidfile
and want to call it like ./test.sh | otherprogram, e. g. ./test.sh | cat.
The pipe is not being closed as the background process still exists and might produce some output. How can I tell the pipe to close at the end of test.sh? Is there a better way than checking for existence of pidfile before calling the pipe command?
As a variant I tried using #!/bin/bash and disown at the end of test.sh, but it is still waiting for the pipe to be closed.
What I actually try to achieve: I have a "status" script which collects the output of various scripts (uptime, free, date, get-xy-from-dbus, etc.), similar to this test.sh here. The output of the script is passed to my window manager, which displays it. It's also used in my GNU screen bottom line.
Since some of the scripts that are used might take some time to create output, I want to detach them from output collection. So I put them in a while true; do script; sleep 1; done loop, which is started if it is not running yet.
The problem here is now that I don't know how to tell the calling script to "really" detach the daemon process.
See if this serves your purpose:
(I am assuming that you are not interested in any stderr of commands in while loop. You would adjust the code, if you are. :-) )
#!/bin/bash
if [ -f pidfile ] && kill -0 $(cat pidfile); then
cat somewhere
exit
fi
while true; do
echo "something" >> somewhere
sleep 1
done >/dev/null 2>&1 &
echo $! > pidfile
If you want to explicitly close a file descriptor, like for example 1 which is standard output, you can do it with:
exec 1<&-
This is valid for POSIX shells, see: here
When you put the while loop in an explicit subshell and run the subshell in the background it will give the desired behaviour.
(while true; do
echo "something" >> somewhere
sleep 1
done)&
I am trying to write a wrapper which will execute a script as a session leader.
I am confused by the behaviour of the linux command setsid. Consider this script, called test.sh:
#!/bin/bash
SID=$(ps -p $$ --no-headers -o sid)
if [ $# -ge 1 -a $$ -ne $SID ] ; then
setsid bash test.sh
echo pid=$$ ppid=$PPID sid=$SID parent
else
sleep 2
echo pid=$$ ppid=$PPID sid=$SID child
sleep 2
fi
The output differs depending on whether it is executed or sourced:
$ bash
$ SID=$(ps -p $$ --no-headers -o sid)
$ echo pid=$$ ppid=$PPID sid=$SID
pid=9213 ppid=9104 sid= 9104
$ ./test.sh 1 ; sleep 5
pid=9326 ppid=9324 sid= 9326 child
pid=9324 ppid=9213 sid= 9104 parent
$ . ./test.sh 1 ; sleep 5
pid=9213 ppid=9104 sid= 9104 parent
pid=9336 ppid=1 sid= 9336 child
$ echo $BASH_VERSION
4.2.8(1)-release
$ exit
exit
So, it seems to me that setsid returns immediately when the script is sourced, but it waits for its child when the script is executed.
Why would the presence of a controlling tty have anything to do with setsid? Thanks!
Edit: For clarification I added pid/ppid/sid reporting to all relevant commands.
The source code of the setsid utility is actually very straightforward. You'll note that it only fork()s if it sees that its process ID and process-group ID are equal (i.e., if it sees that it's a process group leader) — and that it never wait()s for its child process: if it fork()s, then the parent process just returns immediately. If it doesn't fork(), then it gives the appearance of wait()ing for a child, but really what happens is just that it is the child, and it's Bash that's wait()ing (just as it always does). (Of course, when it really does fork(), Bash can't wait() for the child it creates, because processes wait() for their children, not their grandchildren.)
So the behavior that you're seeing is a direct consequence of a different behavior:
when you run . ./test.sh or source ./test.sh or whatnot — or for that matter, when you just run setsid directly from the Bash prompt — Bash will launch setsid with a new process-group-ID for job control purposes, so setsid will have the same process-ID as its process-group-ID (that is, it's a process group leader), so it will fork() and won't wait().
when you run ./test.sh or bash test.sh or whatnot and it launches setsid, setsid will be part of the same process group as the script that's running it, so its process-ID and process-group-ID will be different, so it won't fork(), so it'll give the appearance of waiting (without actually wait()ing).
The behavior I observe is what I expect, though different from yours. Can you use set -x to make sure you're seeing things right?
$ ./test.sh 1
child
parent
$ . test.sh 1
child
$ uname -r
3.1.10
$ echo $BASH_VERSION
4.2.20(1)-release
When running ./test.sh 1, the script's parent — the interactive shell — is the session leader, so $$ != $SID and the conditional is true.
When running . test.sh 1, the interactive shell is executing the script in-process, and is its own session leader, so $$ == $SID and the conditional is false, thus never executing the inner child script.
I do not see any problem with your script as is. I added extra statements in your code to see what is happening:
#!/bin/bash
ps -H -o pid,ppid,sid,cmd
echo '$$' is $$
SID=`ps -p $$ --no-headers -o sid`
if [ $# -ge 1 -a $$ -ne $SID ] ; then
setsid bash test.sh
echo pid=$$ ppid=$PPID sid=$SID parent
else
sleep 2
echo pid=$$ ppid=$PPID sid=$SID child
sleep 2
fi
The case that concerns you is:
./test.sh 1
And trust me run this modified script and you will see exactly what is happening. If the shell which is not a session leader runs the script then it simply goes to else block. Am I missing something?
I see now what you mean: When you do ./test.sh 1 with your script as is then parent waits for the child to complete. child blocks the parent. But if you start the child in background then you will notice that parent completes before child. So just make this change in your script:
setsid bash test.sh &