Bash script to schedule multiple jobs - linux

I wonder if it is possible to write a bash script that would do the following:
make firstprogram
which compiles and executes the first program. Than it would wait until this program is done and then execute:
make secondprogram
How can I write the bash script so that it is run in the terminal?

Is this what you intend? It will finish the first command before running the next. If you only want to run the second if the first runs successfully (exits with exit code 0), use && instead of ;
#!/bin/bash
make firstprogram; make secondprogram

You need to utilize the wait command
#!/bin/bash
make firstprogram
firstprogram &
wait
echo "First program done!"
make secondprogram
secondprogram &
echo "Second program done!"
exit 0

Related

Calling `ksh` from `ksh` script stops execution

I am executing a ksh script from another ksh script. The called script ends by executing ksh which simply stops the caller script from continuing.
MRE
#!/bin/ksh
# Caller Script
. ~/called
# Does not continue to next echo.
echo "DONE!"
#!/bin/ksh
#Called script
# Some exports..
ENV=calledcalled ksh
Output with set -x
++ ksh
++ ENV=calledcalled
.kshrc executed
If I run calledcalled directly in my caller it works fine (i.e. continues with next commands. Why does this happen? I checked $? and it is 0. I tried ./called || true. Please let me know if more information is needed.
Note: Called script is outside my control.
This is completely normal and expected. Remember, when you run cmd1; cmd2, cmd2 doesn't run until cmd1 exits.
When your script runs ksh (and is invoked from a terminal or other context where reading from stdin doesn't cause an immediate EOF), nothing is making that new copy of ksh exit -- it waits for code to run to be given to it on stdin as normal -- so that script is just sitting around waiting for the copy of ksh to exit before it does anything else.
There are plenty of ways you can work around this. A few easy ones:
Ensure that stdin is empty so the child interpreter can't wait for input
. ~/called </dev/null
Define a function named ksh that doesn't do anything at all.
ksh() { echo "Not actually running ksh" >&2; }
. ~/called
Set ENV (a variable which, when defined, tells any shell to run the code in that file before doing anything else) to the filename of a script that, when run, causes any interactive shell to exit immediately.
exit_script=$(mktemp -t exit_script.XXXXXX)
printf '%s\n' 'case $- in *i*) exit 0;; esac' >"$exit_script"
ENV=$exit_script . ~/called
rm -f -- "$exit_script"
The above are just a few approaches; you can surely imagine many more with just a little thought and experimentation.

Executing bash shell script accepting argument and running in background

I have written a shell script run.sh to trigger a few tasks based on the user's input
#!/bin/bash
echo "Please choose mode [1-3]: "
read MODE
case $MODE in
1)
echo -n "Enter iteration: "
read TIME
echo "Start Updating ..."
task 1 && task 2 && task 3
;;
2)
echo -n "Enter Seed Value: "
read SEED
echo "Start Updating ..."
task 4
task 5
;;
3)
echo -n "Enter regression minimum value: "
read MIN
echo "Start Updating ..."
task 6
;;
*)
echo -n "Unknown option - Exit"
;;
esac
The tasks 1,2 ... 6 are php scripts that are run like /usr/bin/php task1.php $TIME with $TIME as an argument for the php script etc...
The script runs fine when I type bash run.sh but since tasks 1-6 takes a long time to complete I would like an option to run the script in background while I disconnect from the terminal. However if I run the script using bash run.sh & I encountered an error like this:
Please choose mode [1-3]: 2
-bash: 2: command not found
[5]+ Stopped bash run.sh
It seems like bash interpreted my input 2 as an argument not corresponding to read MODE but instead of bash run.sh 2 which causes an error. I cannot change the script such that tasks 1-6 are run in the background like task 1 & task 2 & etc. because task 2 can only start running after task 1 is completed.
How can I accomplish what I want to do?
You could run all of those tasks sequentially in a background subshell
( task 1; task 2; task 3 ) &
Try this out with:
( echo "One"; sleep 1; echo "Two"; sleep 2; echo "Three"; sleep 3; echo "Done" ) &
You could also make this more script-looking:
(
echo "One"
sleep 1
echo "Two"
sleep 2
echo "Three"
sleep 3
echo "Done"
) &
Feel free to make use of the useful envar $BASH_SUBSHELL
( echo $BASH_SUBSHELL; ( echo $BASH_SUBSHELL ) )
Short answer: running interactive programs (/scripts) in the background doesn't really work; it'll work even less well if you disconnect from the terminal. You should rewrite the script so it doesn't need user input as it runs.
Long answer: when you run the script in the background, something like this happens. Note that the exact order of events may vary, as both the background script and foreground interactive shell are running at the same time, "racing" each other to get things done.
You start the script in the background with bash run.sh &
Your interactive shell is immediately ready for your next command, so it prints your usual command prompt to the terminal.
Your script prints its prompt ("Please choose mode [1-3]: ") to the terminal.
Your interactive shell reads its next command from the terminal. Because the script's prompt printed second, it looks like you're sending input to it, but there's actually no connection between the most recent prompt and which program is receiving your input.
Your interactive shell attempts to run your input ("2") as a command, and it fails.
Your shell script finally gets its chance to read from the terminal... but it's in the background, so it's not allowed to. Instead, it is suspended, and a "Stopped" message is printed. From the bash man page, "Job Control" section:
Background processes which attempt to read from (write to when stty tostop
is in effect) the terminal are sent a SIGTTIN (SIGTTOU) signal
by the kernel's terminal driver, which, unless caught, suspends the
process.
At this point, if you want to continue the script and tell it what to do, you'd need to move it to the foreground (e.g. with the fg) command. Which would kind of negate the point here. Also, its prompt ("Please choose mode [1-3]: ") will not be repeated, as that echo command successfully finished while it was in the background.
The solution: basically, write the script to non-interactively run the tasks in the necessary order. #Lenna has given examples; follow her recommendations.

Bash call external command and return control on exit

Is there a proper way to call an external command or script in Bash, wait for completion, and return control back to the calling script? What I am seeing is that when the external script exits, it just drops to a shell without returning control:
#!/bin/bash
while [[ "${RUNAGAIN,,}" != *"no"* ]]
do
$cmd="source ./otherscript.sh"
fg $cmd
echo "Do you want to run again?"
read RUNAGAIN
done
Thanks for the help. Running just "source ./externalscript.sh" did the trick for me. Not sure why it wasn;t working to begin with, but everything is good now.

Bash Switch Statement and Executing Multiple Programs

I'm trying to use a Bash switch statement to execute a set of programs. The programs are run through the terminal via a script. The simple idea is ::
In the terminal : ./shell.sh
Program asks : "What number?"
I input : 1
Program processes as:
prog="1"
case $prog in
1) exec gimp && exec mirage ;;
esac
I've tried it several ways yet nothing will run the second program and free the terminal. The first program runs fine and frees the terminal after closing. What am I to put after executing the first program that will allow the second to run in tandem with the first and also free the terminal?
To run two commands in the background, use & after each of them:
case $prog in
1)
gimp &
mirage &
;;
esac
exec basically means "start running this program instead of continuing with this script"

What is the difference between & and | in linux?

I understand | pipes the output of the first command into the stdin of the second command. How does & relate two processes?
Probably you want to know about && not & (which is for executing a command in background)
This command:
command1 | command2
Means pass output of command1 as input (stdin) of command2
But in this command:
command1 && command2
Means execute command2 ONLY if command1 is successful
Single amperstand (&) is used for backgrounding. It makes the command run in the background.
Also from man bash :
If a command is terminated by the control operator &, the shell
executes the command in the background in a subshell. The shell does
not wait for the command to finish, and the return status is 0.
So if you write something like this:
find -name hello &
This will make the find command to be forked and run in the background
In your case single amperstand & does not relate to the two process in any way.
You are probably looking for
command1 && command2
& does not relate two processes in any way; it starts a process in the "background" so that the shell you are running continues its work without waiting for the process to terminate like it normally would.
You are probably thinking of &&. The command line
command 1 && command 2
executes first command 1, and if it is successful (exits with status code 0) it executes command 2. The exit status of the compound is the exit status of the first command if unsuccessful, otherwise the exit status of 2.
For example, the following command line can be used to install Unix software from source, but only if it is successfully configured, compiled, and all tests run.
./configure && make && make test && make install

Resources