Send automatic input to a script called by another script in bash - linux

I'm working on a bash script (my_script) in which I call many scripts, they all together automate a work flow.
But when I call one particular (ksh/bash) script (master_script) there are many inputs and checks taken (not arguments) in it.
It is slowing down the whole of the automation, as every time I have to super wise it and enter the values manually.
I have no option to modify or make a new script (work constraints)
Every time the questions are same. I am trying to take all the answers before executing master_script except one answer(whose value depends on the execution) and then feed it to the master_script at the correct time.
Is there a way we can pass the value to the master_script, during its execution from within my_script.? ./master_script<< EOF .. EOF will not help as I have to enter one answer myself.
The below is just an example and my creation, but depicts what exactly is my requirement.
Example code
my_script
#! /bin/bash
echo "Proceeding...."
#calling master_script
/master_script $arg1 $arg2
echo "Completed.."
echo "Executing other scripts"
/other_scripts"
Execution
$ sh ./my_script
Proceeding....
Started master_script..
Press Enter to Proceed MY_INPUT
Enter username to add (eg.user123) MY_UNAME
Enter preferred uid (eg.1234) MY_UID
Do you want to bla bla..(Y/n) MY_INPUT
Please select among the following
1.option1
2.Option2
Selection: MY_SELECTION
Please choose which extension to use
1.ext1
2.ext2
3.ext3
4.ext4
Do you want to bla bla 2..(Y/n) MY_INPUT
Ended master script
Completed..
Executing other scripts
Requirement
#! /bin/bash
echo "Proceeding...."
# get values for master script
read -p "Proceed(Y/n):" proceed1
read -p "Uname:" uname
read -p "Uid:" uid
read -p "bla bla (Y/n):" bla1
read -p "Selection(1/2):" selection1
read -p "bla bla 2(Y/n):" bla2
#calling master_script
./master_script $arg1 $arg2 {all_inputs}
#Silent Execution of master_script until choosing execution...
Please choose which extension to use
1. ext1
2. ext2
3. ext3
4. ext4
#Silent Execution of master_script after choosing ext and continue with other scripts
./other_scripts
echo "Completed.."
I've read about expect/send combination, but I'm unable to comprehend
how to use it. Any inputs will be greatly helpful
EDIT
I am also not sure about ./master_script<< EOF ... EOF as I have to enter one
answer in the middle of execution myself.

There is a solution using here documents and redirecting the input:
./master_script "$arg1" "$arg2" << ENDINPUT
$proceed1
$uname
$uid
$bla1
$selection1
ENDINPUT
Remark 1: the final ENDINPUT must start the line, don't indent! See Man bash
Remark 2: some scripts or programs check if the input comes from an actual terminal (calling isatty()), for instance when typing a password. It is still possible to automate the entries, but it is much more tricky.

Related

How to create a script that is automated and sequential

I'm struggling in trying to create a script in Linux (Terminal-Bash) which is automated and when run it will ask for a input and when that input is added it will create sequential files.
See below:
when script is run, it should show
Which country are you from?
I enter Brazil and wish to see:
Data entered: Brazil1, Brazil2, Brazil3.....Brazil 10
The script needs create a batch of 10 each time it is run i.e.
Which country are you from?
I enter Canada and wish to see:
Canada1, Canada2, Canada3.....Canada10
If brazil entered again: Brazil11, Brazil12....Brazil20 and Brazil21, Brazil22....Brazil30 etc
I do not want to hardcode the numbers, automation is required to create them each time.
**Testing:
**
I have created the script by:
Touch test.sh
I have tried to edit the script by:
vim test.sh
In vim I have made the below changes:
#!bin/bash
echo "Which country are you from"
value=country
value{1..10}
Save vim, when executing script I get this message:
test.sh: line 4: value1: command not found
Can someone please help with the script? I'm completely new to linux and trying to best understand how to create the simplest and most effective process.
Thanks in advance.
This script is pretty straightforward to implement. You need to use the read command to read user input in. You can use the while loop with -f to test which files already exist, and then finally the for loop to create those files
#!/bin/bash
echo Hello, what country are you from?
read user_country
n=1
while [ -f "$user_country$n" ]
do
let "n+=10"
done
for ((i=0; i<10; i++))
do
file_num=$((i + n))
touch "$user_country$file_num"
done

How to use one environment variable when calling a bash script repeatedly

I have a task to monitor the system with a quota, if the monitored result is over the quota, send a warning email. But this monitor program should be called once in half an hour, after one warning email is sent out, the next time if the monitored state is still the same as last time, there is no need to send the same warning email again.
In order to do this, I would like to make use of environment variable to store the state of the last monitored result, so that the next time it can be checked and duplicate email would not be sent. One of my solution is to add or update the export syntax in .bashrc, but in order to activate the updated export syntax, I have to run bash, which might be unnecessary.
So I would like ask is there any way to update the environment variable so that every time when the monitor program Bash script is called, it gets the fresh updated value?
This is a self contained solution using a heredoc. At first glance it may seem an elaborate and inperfect solution, it does have its uses in that it's resilient and it works well when deploying across more than one machine, requires no special monitoring or permissions of external files, and most importantly, there are no unwanted surprises with environment.
This example uses bash, but it will work with sh if the $thisfile variable is set manually, or another way.
This example assumes that 20 is already in the script file as mymonitorvalue, and uses argument $1 as a proof of concept. You would obviously change newval="$1" to whatever calculates the quota:
Example usage:
#bash $>./script 10
Value from previous run was 20
Value from this test was 10
Updating value ...
#bash $>./script 10
not doing anything ... result is the same as last time
#bash $>
Script:
#!/bin/bash
thisfile="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" ; thisfile="${DIR}${0}"
read -d '' currentvalue <<'EOF'
mymonitorval=20
EOF
eval "$currentvalue"
function changeval () {
sed -E -i "s/(^mymonitorval\=)(.*)/mymonitorval\="$1"/g" "$thisfile"
}
newvalue="$1"
if [[ "$newvalue" != "$mymonitorval" ]]; then
echo "Value from previous run was $mymonitorval"
echo "Value from this test was "$1""
echo "Updating value ..."
changeval "$newvalue"
else
echo "not doing anything ... result is the same as last time"
fi
Explanation:
thisfile= can be set manually for script location. This example uses the automated solution from here: https://stackoverflow.com/a/246128
read -d...EOF is the heredoc which is saved into variable $currentvalue
eval "$currentvalue" in this case is the equivalent of typing mymonitorval=10 into a terminal
function changeval...} updates the contents of the heredoc in place (it changes the physical .sh script file)
newvalue="$1" is for the purpose of testing. $newvalue would be determined by whatever your script is that is calculating quota
if.... block is to perform two alternate sets of actions depending on whether $newvalue is the same as it was last time or not.
Store environment variable in different .file and then source <.file>

Linux bash script - For loops issues

I'm working on a bash script that will add users in a batch process. This code goes as follows:
#!/bin/bash
# A script that creates users.
echo "This is a script to create new users on this system."
echo "How many users do you want to add?"
read am
echo " "
for i in {0..$am..1}
do
echo "Enter a username below:"
read usern
sudo useradd $usern
sudo passwd $usern
echo " "
echo "User $am '$usern' added."
done
In this case, I wanted to make 4 users. I went through and entered the username "callum3" and set the password as "1234" for ease of login. Once I input everything (correctly, may I add) the terminal window displays the following.
User 4 'callum3' added.
This shows that my for loop isn't actually working, when I can see nothing wrong with it. I have tried using a while loop with no luck there either.
Am I making a rookie mistake here or is there something deeper going on?
Although I suspected it, for a better understanding on what could be wrong with your script I pasted it in shellcheck.net. That the problem is in the line:
for i in {0..$am..1}
Bash doesn't support variables in brace range expansions. That is, you cannot use a variable in an expression like {..}.
Instead, use seq. With seq $var you get a sequence from 1 (default) to $var:
for i in $(seq "$am")
I feel like I'm missing something in that nobody has suggested an arithmetic for loop:
for ((i=0; i<am; i++)); do
…
done
This has the particular benefit in bash of being both readable and not requiring a subshell.
You can use:
for i in `seq 0 $((am-1))`
do
...
done
Sequence will start from 0 and end at $am-1

read command is not taking input from the terminal

I dont know if it is weird that read is not taking the input from the terminal.
The configure script, which is used in source code making process, should ask the user to give the input to select the type of Database either MYSQL or ORACLE(below is the code).
MYSQLLIBPATH="/usr/lib/mysql"
echo "Enter DataBase-Type 1-ORACLE, 2-MySQL (default MySQL):"
read in
echo $? >> /tmp/error.log
if test -z "$in" -o "$in" = "2"
then
DATABASE=-DDB_MYSQL
if true; then
MYSQL_TRUE=
MYSQL_FALSE='#'
else
MYSQL_TRUE='#'
MYSQL_FALSE=
fi
echo "Enter Mysql Library Path: (eg: $MYSQLLIBPATH (default))"
read in
echo $? >> /tmp/error.log
if test -n "$in"
then
MYSQLLIBPATH=`echo $in`
fi
echo "Mysql Lib path is $MYSQLLIBPATH"
else
if false; then
MYSQL_TRUE=
MYSQL_FALSE='#'
else
MYSQL_TRUE='#'
MYSQL_FALSE=
fi
DATABASE=-DDB_ORACLE
LD_PATH=
fi
But, the read command is not asking for the user input. Its failing to take the input from the stdin.
When I checked the status of the command in the error.log it was showing
1
1
Could anyone tell why read is failing to take the input from the stdin.
Are there any builtin variable which can block read taking the input?
Most likely read executes with standard input redirected from a file that has reached EOF. If the above is not the whole of your configure code, check that there are no input redirections. Could the code above be a part of a function which was invoked with some input from a pipe or a file? Otherwise check how configure is executed - are there any redirections?
Otherwise, the universal advice applies: try simplifying and stripping down your code until it is obvious what's happening.
BTW, it is not a good idea to make configure interactive, if you want to have your program packaged for a distribution - it's not easy to control execution of interactive programs. Consider adding support for supplying parameters through command line options.

Echo text that is user-editable

Is it possible to output text to a shell window, via bash script, that is user-editable? I essentially want to pre-fill certain information and give the user the ability to edit it if it's wrong.
For instance, if I were to write in a script:
echo -n "Enter your name: Anthony"
while read user_input
do
# do stuff with $user_input
done
How can I allow the user to inline edit the word Anthony only (aka, don't allow backspacing past the A in Anthony), and how can I store the value into a variable once the RETURN key is pressed?
EDIT
I'm looking for something similar to the -i option of read (see answer posted here), but this is only available on bash 4+. Is there an alternative for bash 3?
I needed similar setup recently so what I did was
$ cat a.sh
function input {
python -c '
import sys,readline
readline.set_startup_hook(lambda: readline.insert_text(sys.argv[2]))
sys.stderr.write(raw_input(sys.argv[1]))
' "$#" 3>&1 1>&2 2>&3
}
A=$( input 'question: ' default )
echo "A='$A'"
$ ./a.sh
question: default
A='default'
Well, it's not actually bash, but it made the job done.

Resources