Start background process from shellscript then bring back to foreground later - shell

I'm trying to make a shell script that does the following:
Start program x
While x is running execute some commands, for example:
echo "blabla" >> ~/blabla.txt
After the execution of those commands program x should be running in the foreground, so that it can take user input.
So far I have:
~/x &
echo "blabla" >> ~/blabla.txt
However, I don't know how to move x back to the foreground. This is all called from a shell script so I don't know the job number of x to move to the foreground.
Note: everything has to be automated, no user interaction with the shell script should be needed.
Any suggestions are welcome :)

Although absolutely don't understand why someone may need such script, and I'm sure than exists more elegant and more better/correct solution - but ok - the next demostrating how:
The script what going to background (named as bgg)
#!/bin/bash
for i in $(seq 10)
do
echo "bg: $i"
sleep 1
done
read -p 'BGG enter something:' -r data
echo "$0 got: $data"
the main script (main.sh)
set -m #this is important
echo "Sending script bgg to background - will cycle 10 secs"
./bgg & 2>/dev/null
echo "Some commands"
date
read -r -p 'main.sh - enter something:' fgdata
echo "Main.sh got: ==$fgdata=="
jnum=$(jobs -l | grep " $! " | sed 's/\[\(.*\)\].*/\1/')
echo "Backgroung job number: $jnum"
echo "Now sleeping 3 sec"
sleep 3
echo "Bringing $jnum to foreground - wait until the BG job will read"
fg $jnum
run the ./main.sh - and the result will be something like
Sending bgg to background - will cycle 10 secs
Some commands
Mon Mar 3 00:04:57 CET 2014
main.sh - enter something:bg: 1
bg: 2
bg: 3
bg: 4
bg: 5
qqbg: 6
qqqqq
Main.sh got: ==qqqqqqq==
Backgroung job number: 1
Now sleeping 3 sec
bg: 7
bg: 8
bg: 9
Bringing 1 to foreground - wait until the BG job will read
./bgg
bg: 10
BGG enter something:wwwwwww
./bgg got: wwwwwww

You can use fg to bring the last background process to foreground

Related

Issues with script run from /etc/rc.local

I'm trying to run a bash script at boot time from /etc/rc.local on a headless Raspberry Pi 4 (Raspbian buster lite - Debian based). I've done something similar on a Pi 3 with success so I'm confused about why the Pi 4 would misbehave - or behave differently.
The script executed from /etc/rc.local fires but appears to just exit at seemingly random intervals with no indication as to why it's being terminated.
To test it, I dumbed down the script and just stuck the following into a test script called /home/pi/test.sh:
#!/bin/bash
exec 2> /tmp/output # send stderr from rc.local to a log file
exec 1>&2 # send stdout to the same log file
set -x # tell bash to display commands before execution
while true
do
echo 'Still alive'
sleep .1
done
I then call it from /etc/rc.local just before the exit line:
#!/bin/sh -e
#
# rc.local - executed at the end of each multiuser runlevel
#
# Make sure that the script will "exit 0" on success or any other
# value on error.
/home/pi/test.sh
echo $? >/tmp/exiterr #output exit code to /tmp/exiterr
exit 0
The contents of /tmp/output:
+ true
+ echo 'Still alive'
Still alive
+ sleep .1
+ true
+ echo 'Still alive'
Still alive
+ sleep .1
and /tmp/exiterr shows
0
If I reduce the sleep period, /tmp/output is longer (over 6000 lines without the sleep).
Any ideas why the script is exiting shortly after starting?

How to save multiple $! into variables and use it later in bash?

I want to get the pids of two background processes,
sleep 20 & pid1=$\!; sleep 10 & pid2=$\!; echo "pid1: $pid1, pid2: $pid2"
and get output like below:
[1] 124646
[2] 124648
pid1: $!, pid2: $!
the output I desired to get is like:
pid1: 124646, pid2: 124648
Anyone know why and can help to achieve this?
[add 2018/01/02]
Sorry for really late response, one hand is busy, another is that I want to verify the script.
The actual script I want to run is like below:
sh -c "sleep 20 & pid1=$!; sleep 10 & pid2=$!; echo \"pid1: $pid1, pid2: $pid2\""
and as it will report bash: !: event not found, so I tried to add \ and become:
sh -c "sleep 20 & pid1=$\!; sleep 10 & pid2=$\!; echo \"pid1: $pid1, pid2: $pid2\""
and for make the problem simple, I just rmeove sh -c while this make it a quite different problem.
for my problem, I found out that below script will work:
sh -c 'sleep 20 & pid1=$!; sleep 10 & pid2=$!; echo "pid1: $pid1, pid2: $pid2"'
Yet there is another question, how to make below script to work:
name='bob'
# below script reports 'bash: !: event not found' error
sh -c "echo $name; sleep 20 & pid1=$!; sleep 10 & pid2=$!; echo \"pid1: $pid1, pid2: $pid2\""
# below script $name will not become bob
sh -c 'echo $name; sleep 20 & pid1=$!; sleep 10 & pid2=$!; echo \"pid1: $pid1, pid2: $pid2\"'
The backslash is causing the value to be the string $! verbatim. Don't put a backslash in the assignment.
On the command line, you may want to temporarily set +H to avoid getting event not found warnings; but this only affects the interactive shell. In a script, set -H is never active (and would be meaningless anyway).
(I'm speculating this is the reason you put the backslash there in the first place. If not, simply just take it out.)
Your syntax kindly incorrect, try this:
[root#XXX ~]# sleep 5 & pid1=$!; sleep 6 & pid2=$!; echo "pid1: ${pid1}, pid2: ${pid2}"
[1] 2308
[2] 2309
pid1: 2308, pid2: 2309
I have accomplished this in the past by using bash arrays to hold the PIDs. I had a sequence of database imports to run and when handled sequentially they took ~8 hours to complete. I launched them all as background processes and tracked the list of PIDs to watch for completion and it got the processing time down to 45 minutes.
Here is an example of launching background processes, storing the PIDs in an array, and then printing all of the array values:
$ pids=()
$ sleep 20 &
22991
$ pids+=($!)
$ sleep 20 &
23298
$ pids+=($!)
$ j=0;for i in "${pids[#]}";do ((j=j+1));echo 'pid'$j': '$i;done
pid1: 22991
pid2: 23298

How can I wait for a file to be finished being written to in shell script?

I have a shell script called parent.sh which does some stuff, then goes off and calls another shell script child.sh which does some processing and writes some output to a file output.txt.
I would like the parent.sh script to only continue processing after that output.txt file has been written to. How can I know that the file has finished being written to?
Edit: Adding answers to questions:
Does child.sh finish writing to the file before it exits? Yes
Does parent.sh run child.sh in the foreground or the background? I'm not sure - it's being called from withing parent.sh like this: ./child.sh "$param1" "$param2"
You need the wait command. wait will wait until all sub-processes have finished before continuing.
parent.sh:
#!/bin/bash
rm output.txt
./child.sh &
# Wait for the child script to finish
#
wait
echo "output.txt:"
cat output.txt
child.sh:
#!/bin/bash
for x in $(seq 10); do
echo $x >&2
echo $x
sleep 1
done > output.txt
Here is the output from ./parent.sh:
[sri#localhost ~]$ ./parent.sh
1
2
3
4
5
6
7
8
9
10
output.txt:
1
2
3
4
5
6
7
8
9
10

How do I pause my shell script for a second before continuing?

I have only found how to wait for user input. However, I only want to pause so that my while true doesn't crash my computer.
I tried pause(1), but it says -bash: syntax error near unexpected token '1'. How can it be done?
Use the sleep command.
Example:
sleep .5 # Waits 0.5 second.
sleep 5 # Waits 5 seconds.
sleep 5s # Waits 5 seconds.
sleep 5m # Waits 5 minutes.
sleep 5h # Waits 5 hours.
sleep 5d # Waits 5 days.
One can also employ decimals when specifying a time unit; e.g. sleep 1.5s
And what about:
read -p "Press enter to continue"
In Python (question was originally tagged Python) you need to import the time module
import time
time.sleep(1)
or
from time import sleep
sleep(1)
For shell script is is just
sleep 1
Which executes the sleep command. eg. /bin/sleep
Run multiple sleeps and commands
sleep 5 && cd /var/www/html && git pull && sleep 3 && cd ..
This will wait for 5 seconds before executing the first script, then will sleep again for 3 seconds before it changes directory again.
I realize that I'm a bit late with this, but you can also call sleep and pass the disired time in. For example, If I wanted to wait for 3 seconds I can do:
/bin/sleep 3
4 seconds would look like this:
/bin/sleep 4
On Mac OSX, sleep does not take minutes/etc, only seconds. So for two minutes,
sleep 120
Within the script you can add the following in between the actions you would like the pause. This will pause the routine for 5 seconds.
read -p "Pause Time 5 seconds" -t 5
read -p "Continuing in 5 Seconds...." -t 5
echo "Continuing ...."
read -r -p "Wait 5 seconds or press any key to continue immediately" -t 5 -n 1 -s
To continue when you press any one button
for more info check read manpage ref 1, ref 2
You can make it wait using $RANDOM, a default random number generator. In the below I am using 240 seconds. Hope that helps #
> WAIT_FOR_SECONDS=`/usr/bin/expr $RANDOM % 240` /bin/sleep
> $WAIT_FOR_SECONDS
use trap to pause and check command line (in color using tput) before running it
trap 'tput setaf 1;tput bold;echo $BASH_COMMAND;read;tput init' DEBUG
press any key to continue
use with set -x to debug command line

Piping to a command in Ash Shell

I wrote a bash script to send an email using telnet. I'm installing it on a TS-7260 running busyBox (which has an ash shell).
Something is different between Bash and Ash and I can't figure out why the following won't work. It's got to be something with the way I'm piping the echos to telnet. Here's the script:
#!/bin/ash
# Snag all the error messages from a given date, open a telnet connection to an outgoing mail server, stick the logs in an email, and send it.
# Tue Jul 2 14:06:12 EDT 2013
# TMB
# Tue Jul 9 17:12:29 EDT 2013
# Grepping the whole error file for WARNING and the piping it to a grep for the date took about four minutes to complete on the gateway. This will only get longer and the file will only get bigger as time goes by.
# Using tail to get the last 5000 lines, I get about three days of errors (2000 of them are from one day, though)
# Getting 5000 lines, then searching them by WARNING and then DATE took 15 seconds on the gateway.
yesterdayDate=$(./getYesterday)
warningLogs=$(tail -5000 /mnt/sd/blah.txt | grep WARNING | grep "$yesterdayDate")
sleep 30
{
sleep 5
echo "ehlo blah.com"
sleep 5
echo "auth plain blah"
sleep 5
echo "mail from: blah#blah.com"
sleep 5
echo "rcpt to: me#blah.com"
sleep 5
echo "data"
sleep 5
echo "Hi!"
sleep 1
echo "Here are all the warnings and faults from yesterday:"
sleep 1
echo "$yesterdayDate"
sleep 1
echo "NOTE: All times are UTC."
sleep 1
echo ""
sleep 1
echo "$warningLogs"
sleep 10
echo ""
sleep 1
echo "Good luck,"
sleep 1
echo "The Robot"
sleep 5
echo "."
sleep 20
echo "quit"
sleep 5
} | telnet blah.com port
exit
I've tried using normal parentheses too before the pipe. I've read the man page for ash and am still doing something stupid. I suspect it's some kind of child process business going on.
This works fine from bash, btw.
Thanks in advance!
Note -- I simplified the script to be just:
echo "quit" | telnet blah.com port
It does exactly what you'd expect in bash, but I see nothing happen in ash.
Replacing the echo with "sleep 10" shows sleep running as a process, but not telnet.
After some more experimentation, the problem was not with the shell at all, but with the implementation of Telnet on Busybox. On my version of BusyBox (1.00rc2), piping anything to Telnet didn't work.
echo blah | telnet -yrDumb
Should have at least made telnet complain about usage. It didn't.
I grabbed the most recent version of inetutils (1.9.1) and compiled its telnet for the TS-7260. It works like a dream (read: it works) now, and is consistent with the behavior I see using telnet and bash on my normal linux box.
Thanks for the help!

Resources