How to send emails in Bash with a for loop? - bash

I have three arrays of the same size: outputs as the body of the email, job_id, which is used in the subject, and email with a list of emails.
However, I'm getting an error when I run my script:
email: FATAL: Smtp error: 555 5.5.2 Syntax error.
Is there a way to reference array elements for the email parameter? This code works when I put an actual email, not "${email[$i]}"
for (( i=1;i<${#outputs[#]};i++ )); do
echo "${outputs[$i]}" | email -s "Job #${job_id[$i]} completed" "${email[$i]}"
done

Related

Returning a filename from a ksh function gives a 'bad number' error

I have a shell script with a function and when I call the function I am getting a bad number error. In my function I am getting a filename from the database and returning it. The return gives the 'bad number' error. Details below:
call to GET_MYFILE() :
GET_MYFILE $jobid
--$job id has the correct number in it
GET_MYFILE()
{
echo "job id input parameter is : " $1
curfile=`sqlplus -s /#username<< EOF
set feed off heading off verify off serveroutput off
select my_file_name
from my_table_name
where jobid=$1;
exit;
EOF`
echo "curfile is : " $curfile
return $curfile
}
-- echo "curfile is : " $curfile - displays correct filename
--return $curfile gives bad number error.
In the Korn Shell syntax, the return statement syntax offers only two variants:
return (and in this case, the returned number is the exit code of the most recently executed command in the function).
return n (where n is a number between 0 and 255)
In either case, the caller can access the numeric value returned via the $? pseudovariable in an assignment or conditional statement.
If you try to return a filename then you should expect "bad number", because a filename string cannot usually be interpreted as a number.

How can i do that in gmail adress cannot be two or more "."(points)?

I have to write something in my if clause, but I don't know how to write it. For example "hello..hello#gmail.com" or "hello.hello.hello#gmail.com" should be invalid inputs, but "hello.hello#gmail.com" should be valid.
read -p "enter email " email;
if [[ "$email" =~ ^[A-Za-z]+[A-Za-z0-9_%+-]+[A-Za-z]+#[A-Za-z]+[A-Za-$
echo "Email address $email is valid."//my if clause, which checks if there is correct gmail
else
echo "Email address $email is invalid."
fi
You can use this expression:
^[A-Za-z][A-Za-z0-9_%+-]+(\.[A-Za-z0-9_%+-]+)?#[A-Za-z]+(\.[A-Za-z]+)+$
Key ideas:
\. allows one dot
(...)? says that group with the dot is optional, so no dots is also OK
minimize the number of different ways the same string can get parsed, for efficiency
I assume the domain can have multiple dots, but you can also use `...#[A-Za-z]+.[A-Za-z]+$ to allow exactly one dot in the domain.

Chef - How to check if a bash block ran successfully and to ignore at the second instances

I am having a chef recipe with the following bash block -
bash "run_bash" do
user node[:value1][:user]
group node[:value1][:group]
cwd node[:value1][:install_dir] + '/tools/bin'
code <<-EOH
export MW_HOME=#{node[:value1][:mw_install_dir]}
export JAVA_HOME=#{node[:value1][:install_dir]}
export ORACLE_HOME=#{node[:value1][:install_dir]}
/usr/bin/expect -c 'spawn ./idmConfigTool.sh -configOAM input_file=#{Chef::Config[:file_cache_path]}/config.props
expect "Enter ID Store Bind DN password : "
send "#{OrcladminPassword}\r"
expect "Enter User Password for IDSTORE_PWD_SOFTWAREUSER: "
send "#{LDAPPassword }\r"
expect "Confirm User Password for IDSTORE_PWD_APPSOFTWAREUSER: "
send "#{AppLDAPPassword }\r"
expect "Enter User Password for IDSTORE_PWD_APP2ADMINUSER: "
send "#{App2AdminPassword }\r"
expect "Confirm User Password for IDSTORE_PWD_APP3ADMINUSER: "
send "#{App3AdminPassword}\r"
sleep 240
expect eof'
EOH
I am trying to run this recipe with chef-solo.So redundantly this recipe will run.
I need a solution to make this bash block to be idempotent. There are guard statements like not_if,only_if etc. But i could not find a way to implement those guards here.
I thought of one solution which is,Once this bash block ran successful , i can find the time stamp and compare it with the current time stamp when the next run is happening. If those didn't match i can ignore running it.This is just a brute force work a round i can think.
Kindly provide a better optimal solution for this scenario.
I'd go with a template an using notifications:
your_cookbook/templates/default/script.erb:
#!/bin/bash
export MW_HOME=<%= node[:value1][:mw_install_dir] %>
export JAVA_HOME=<%= node[:value1][:install_dir] %>
export ORACLE_HOME=<%= node[:value1][:install_dir]}
/usr/bin/expect -c 'spawn ./idmConfigTool.sh -configOAM input_file=<%=
Chef::Config[:file_cache_path] %>/config.props
expect "Enter ID Store Bind DN password : "
send "<%= #OrcladminPassword %>\r"
expect "Enter User Password for IDSTORE_PWD_SOFTWAREUSER: "
send "<%= #LDAPPassword %>\r"
expect "Confirm User Password for IDSTORE_PWD_APPSOFTWAREUSER: "
send "<%= #AppLDAPPassword %>\r"
expect "Enter User Password for IDSTORE_PWD_APP2ADMINUSER: "
send "<%= #App2AdminPassword %>\r"
expect "Confirm User Password for IDSTORE_PWD_APP3ADMINUSER: "
send "<%= #App3AdminPassword %>\r"
sleep 240
expect eof
you_cookbook/recipes/default.rb:
execute 'my_installer' do
command '/usr/local/bin/my_script'
action :noting # For it not to run on each converge by default
end
template '/usr/local/bin/my_script' do
source 'script.erb'
mode '0600'
variables(
'OrcladminPassword' => <how you set the value>,
'LDAPPassword' => <...>,
'AppLDAPPassword' => <...>,
'App2AdminPassword' => <...>,
'App3AdminPassword' => <...>
)
notifies :run,execute[my_installer]
end
You may add a , :immediately at the end of the notifies line to trigger the execution just have the template have been rendered.
This way chef will trigger the execute resource only when the template change.
This doesn't take in account cases where your install may not work and chef won't retry it in this case, the installation will stay failed.
All in all I would work with the product editor to have a kind of package to avoid this kind of brittle install.

for loop always displays a word per line instead of displaying what I want

#!/bin/bash
message="This message is so freaking long that I have to split it into two or more messages"
split=${#message}
array=( ${message:0:5} ${message:5:5} ${message:10:5} ${message:15:5} ${message:20:5})
for i in "${array[#]}"
do
echo "send me a message with \"$i\""
done
The output is like this:
send me a message with "This"
send me a message with "messa"
send me a message with "ge"
send me a message with "is"
send me a message with "so"
send me a message with "f"
send me a message with "reaki"
But I don't want that, I want it like this:
send me a message with "This "
send me a message with "messa"
send me a message with "ge is"
send me a message with "so fr"
send me a message with "eakin"
send me a message with "g lon"
send me a message with "g tha"
How should I fix it? I have googled but I still can't get it to work like what I want, I don't know if the for loop is incorrect or the ${message:x:x} ones.
Use more quotes please!
$ array=( "${message:0:5}" "${message:5:5}" "${message:10 :5}" "${message:15:5}" "${message:20:5}" )
$ for i in "${array[#]}"
> do
> echo "send me a message with \"$i\""
> done
send me a message with "This "
send me a message with "messa"
send me a message with "ge is"
send me a message with " so f"
send me a message with "reaki"
The whitespace in the words were causing them to be treated as separate elements in the array, which was causing the behaviour you observed. Enclosing each element in double quotes prevents this word splitting from happening.

Code to count number of types of files on client in Unix

Hi I am new to shell scripting.
my requirement is:
There is one server & 3 clients. On each client error logs files are generated which are of 4 types.Say type 1 error , type 2 error like this type 4 error.
I want to write a script which read all the 3 clients from server & provide me number of times 4 different type of error logs are genereted on each client.
In short it should use ssh & grep command combination.
I have written the demo script but it's not providing me the number of times different type of logs occured on clients.
#error[1]='Exception: An application error occurred during an address lookup request, please contact IT'
#error[2]='SocketTimeoutException: Read timed out'
#error[3]='Exception: The search has produced too many matches to be returned'
#error[4]='Exception: No matching address found'
error_1='exception 1'
error_2='exception 2'
function get_list_of_clients()
{
NUM_OF_CLIENTS=$(wc -l ${CLIENT_IP_LIST} | awk -F " " '{ print $1 }' )
echo $NUM_OF_CLIENTS
if [ "${NUM_OF_CLIENTS}" -gt 0 ]
then
for ((row=2; row<=$NUM_OF_CLIENTS; row++))
do
CLIENTS_IP=$(sed -n ${row}p ${CLIENT_IP_LIST}| awk -F " " '{print $3 }')
echo ${CLIENTS_IP}
# get_number_of_errors
# copy_count_errors
echo ${$error_$row}
done
fi
}
function get_number_of_errors()
{
for((row_no=1; row_no<=4; row_no++))
do
{
/usr/bin/expect - <<- EndMark
spawn ssh root#${CLIENTS_IP} "grep $error[$row_no] var/error.log |wc -l" >> /tmp/${CLIENTS_IP}_error${row_no}.txt
match_max 50000
expect {
"*yes/no*" {
send -- "yes\r"
send -- "\r"
exp_continue
}
"*?assword:*" {
send -- "${CLIENT_PASSWORD}\r"
send -- "\r"
}
}
expect eof
EndMark
}
done
}
function copy_count_errors()
{
/usr/bin/expect - <<- EndMark
spawn scp root#${CLIENTS_IP}:/tmp/${CLIENTS_IP}* /tmp/
match_max 50000
expect {
"*yes/no*" {
send -- "yes\r"
send -- "\r"
exp_continue
}
"*?assword:*" {
send -- "${CLIENT_PASSWORD}\r"
send -- "\r"
}
}
expect eof
EndMark
}
get_list_of_clients
================================================================================
please help.
This is not really an answer, an attempt to help you getting your own.
The Problem
If I understand it correctly:
Your script runs on the server
You have three clients, each has log files
Your list of clients is in a file named $CLIENT_IP_LIST, where the IP is the third field and the first line is some sort of header, which you want to exclude.
Suggestions
It seems you need to ssh and scp to the clients. If possible, I suggest setting up SSH Public Key Authentication between your server and clients. This will greatly simplify your scripts.
Use the -C flag for compression with the scp and ssh commands.
You should copy the log files from the clients to the server and do processing on the server.
If you can choose a different scripting language such as Python, Tcl (You used Expect, which is really Tcl). Bash can handle your problem, but you will find other languages work better. This is my opinion, of course.
Get one piece of your puzzle to work before moving on to the next. For example, right now, your get_list_of_clients() function does not yet working.
That being said, here is a rewrite of get_list_of_clients, which I tested and it works within my assumptions about your $CLIENT_IP_LIST file:
function get_list_of_clients() {
let row=1
while read line
do
if (( row > 1 ))
then
set $line # Breaks line into pieces
CLIENT_IP="$3"
echo "$CLIENT_IP"
fi
let row=row+1
done < $CLIENT_IP_LIST
}

Resources